From 8b89097c34d28a88dd79f6485f80e7feaa12fca8 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Thu, 13 Feb 2025 09:26:12 -0500 Subject: [PATCH 01/40] Just have packets and testing left for 1.20.1 --- common/build.gradle | 2 + .../azurelib/animatable/GeoBlockEntity.java | 1 + .../azure/azurelib/animatable/GeoEntity.java | 1 + .../azure/azurelib/animatable/GeoItem.java | 1 + .../animatable/GeoReplacedEntity.java | 1 + .../animatable/SingletonGeoAnimatable.java | 1 + .../animatable/client/RenderProvider.java | 1 + .../azure/azurelib/cache/AzureLibCache.java | 15 +- .../azurelib/cache/object/BakedGeoModel.java | 1 + .../azure/azurelib/cache/object/GeoBone.java | 1 + .../azure/azurelib/cache/object/GeoCube.java | 1 + .../azure/azurelib/cache/object/GeoQuad.java | 1 + .../azurelib/cache/object/GeoVertex.java | 1 + .../cache/texture/GeoAbstractTexture.java | 1 + .../azure/azurelib/constant/DataTickets.java | 1 + .../azurelib/constant/DefaultAnimations.java | 1 + .../azure/azurelib/loading/FileLoader.java | 14 + .../loading/object/BakedModelFactory.java | 1 + ...ntityMixin_AzBlockEntityAnimatorCache.java | 32 ++ .../EntityMixin_AzEntityAnimatorCache.java | 32 ++ .../mixins/FabricMixinHumanoidArmorLayer.java | 84 +++- .../ItemStackMixin_AzItemAnimatorCache.java | 27 ++ ...tackMixin_AzItemStackIdentityRegistry.java | 37 ++ .../mixins/NeoMixinHumanoidArmorLayer.java | 44 -- .../model/DefaultedBlockGeoModel.java | 1 + .../model/DefaultedEntityGeoModel.java | 1 + .../azurelib/model/DefaultedGeoModel.java | 1 + .../azurelib/model/DefaultedItemGeoModel.java | 1 + .../mod/azure/azurelib/model/GeoModel.java | 1 + .../azurelib/model/data/EntityModelData.java | 1 + .../network/SerializableDataTicket.java | 1 + .../azure/azurelib/network/api/IPacket.java | 1 + .../azurelib/network/api/IPacketDecoder.java | 1 + .../azurelib/network/api/IPacketEncoder.java | 1 + .../network/packet/AnimDataSyncPacket.java | 1 + .../network/packet/AnimTriggerPacket.java | 1 + .../AzBlockEntityDispatchCommandPacket.java | 74 +++ .../packet/AzEntityDispatchCommandPacket.java | 48 ++ .../AzItemStackDispatchCommandPacket.java | 43 ++ .../packet/BlockEntityAnimDataSyncPacket.java | 1 + .../packet/BlockEntityAnimTriggerPacket.java | 1 + .../packet/EntityAnimDataSyncPacket.java | 1 + .../packet/EntityAnimTriggerPacket.java | 1 + .../azurelib/network/packet/EntityPacket.java | 1 + .../network/packet/EntityPacketOnClient.java | 1 + .../platform/services/AzureLibNetwork.java | 26 + .../renderer/DyeableGeoArmorRenderer.java | 1 + .../renderer/DynamicGeoEntityRenderer.java | 1 + .../azurelib/renderer/GeoArmorRenderer.java | 1 + .../renderer/GeoArmorRendererConstants.java | 1 + .../azurelib/renderer/GeoBlockRenderer.java | 1 + .../azurelib/renderer/GeoEntityRenderer.java | 1 + .../azurelib/renderer/GeoItemRenderer.java | 1 + .../azurelib/renderer/GeoObjectRenderer.java | 1 + .../azure/azurelib/renderer/GeoRenderer.java | 1 + .../renderer/GeoReplacedEntityRenderer.java | 1 + .../dynamic/DynamicGeoArmorRenderer.java | 1 + .../dynamic/DynamicGeoBlockRenderer.java | 1 + .../dynamic/DynamicGeoItemRenderer.java | 1 + .../dynamic/DynamicGeoObjectRenderer.java | 1 + .../DynamicGeoReplacedEntityRenderer.java | 1 + .../renderer/layer/AutoGlowingGeoLayer.java | 1 + .../renderer/layer/BlockAndItemGeoLayer.java | 1 + .../renderer/layer/BoneFilterGeoLayer.java | 1 + .../layer/FastBoneFilterGeoLayer.java | 1 + .../renderer/layer/GeoRenderLayer.java | 1 + .../layer/GeoRenderLayersContainer.java | 1 + .../renderer/layer/ItemArmorGeoLayer.java | 2 +- .../azurelib/rewrite/AzResourceCache.java | 59 +++ .../rewrite/animation/AzAnimationContext.java | 71 +++ .../rewrite/animation/AzAnimationTimer.java | 91 ++++ .../rewrite/animation/AzAnimator.java | 134 +++++ .../rewrite/animation/AzAnimatorAccessor.java | 37 ++ .../rewrite/animation/AzAnimatorConfig.java | 108 +++++ .../animation/AzBoneAnimationUpdateUtil.java | 107 ++++ .../animation/AzCachedBoneUpdateUtil.java | 160 ++++++ .../cache/AzBakedAnimationCache.java | 54 +++ .../rewrite/animation/cache/AzBoneCache.java | 87 ++++ .../AzIdentifiableItemStackAnimatorCache.java | 40 ++ .../animation/cache/AzIdentityRegistry.java | 39 ++ .../AzAbstractAnimationController.java | 33 ++ .../controller/AzAnimationController.java | 210 ++++++++ .../AzAnimationControllerBuilder.java | 64 +++ .../AzAnimationControllerContainer.java | 39 ++ .../AzAnimationControllerTimer.java | 48 ++ .../controller/AzAnimationQueue.java | 51 ++ .../controller/AzBoneAnimationQueueCache.java | 61 +++ .../controller/AzBoneSnapshotCache.java | 53 ++ .../keyframe/AzAbstractKeyframeExecutor.java | 79 +++ .../controller/keyframe/AzAnimationPoint.java | 27 ++ .../controller/keyframe/AzBoneAnimation.java | 20 + .../keyframe/AzBoneAnimationQueue.java | 400 +++++++++++++++ .../controller/keyframe/AzKeyframe.java | 56 +++ .../keyframe/AzKeyframeCallbackHandler.java | 126 +++++ .../keyframe/AzKeyframeCallbacks.java | 105 ++++ .../keyframe/AzKeyframeExecutor.java | 126 +++++ .../keyframe/AzKeyframeLocation.java | 14 + .../keyframe/AzKeyframeManager.java | 47 ++ .../controller/keyframe/AzKeyframeStack.java | 43 ++ .../keyframe/AzKeyframeTransitioner.java | 125 +++++ .../handler/AzCustomKeyframeHandler.java | 14 + .../handler/AzParticleKeyframeHandler.java | 14 + .../handler/AzSoundKeyframeHandler.java | 14 + .../controller/state/AzAnimationState.java | 41 ++ .../state/impl/AzAnimationPauseState.java | 25 + .../state/impl/AzAnimationPlayState.java | 88 ++++ .../state/impl/AzAnimationStopState.java | 25 + .../impl/AzAnimationTransitionState.java | 74 +++ .../AzAnimationControllerStateMachine.java | 105 ++++ .../animation/dispatch/AzDispatchSide.java | 54 +++ .../animation/dispatch/command/AzCommand.java | 175 +++++++ .../dispatch/command/AzCommandBuilder.java | 19 + .../command/AzRootCommandBuilder.java | 53 ++ .../dispatch/command/action/AzAction.java | 25 + .../command/action/codec/AzActionCodec.java | 70 +++ .../action/impl/root/AzRootCancelAction.java | 56 +++ .../impl/root/AzRootCancelAllAction.java | 52 ++ .../AzRootPlayAnimationSequenceAction.java | 52 ++ .../root/AzRootSetAnimationSpeedAction.java | 47 ++ .../impl/root/AzRootSetEasingTypeAction.java | 51 ++ .../root/AzRootSetTransitionSpeedAction.java | 52 ++ .../action/registry/AzActionRegistry.java | 111 +++++ .../command/sequence/AzAnimationSequence.java | 24 + .../sequence/AzAnimationSequenceBuilder.java | 35 ++ .../command/stage/AzAnimationStage.java | 24 + .../animation/easing/AzEasingType.java | 47 ++ .../animation/easing/AzEasingTypeLoader.java | 33 ++ .../easing/AzEasingTypeRegistry.java | 56 +++ .../animation/easing/AzEasingTypes.java | 180 +++++++ .../animation/easing/AzEasingUtil.java | 249 ++++++++++ .../AzCustomInstructionKeyframeEvent.java | 29 ++ .../animation/event/AzKeyframeEvent.java | 65 +++ .../event/AzParticleKeyframeEvent.java | 34 ++ .../animation/event/AzSoundKeyframeEvent.java | 34 ++ .../animation/impl/AzBlockAnimator.java | 19 + .../animation/impl/AzEntityAnimator.java | 69 +++ .../animation/impl/AzItemAnimator.java | 25 + .../parse/AzBakedAnimationsAdapter.java | 364 ++++++++++++++ .../animation/parse/AzKeyframesAdapter.java | 141 ++++++ .../play_behavior/AzPlayBehavior.java | 20 + .../play_behavior/AzPlayBehaviorRegistry.java | 31 ++ .../play_behavior/AzPlayBehaviors.java | 33 ++ .../animation/primitive/AzBakedAnimation.java | 19 + .../primitive/AzBakedAnimations.java | 30 ++ .../animation/primitive/AzKeyframes.java | 23 + .../animation/primitive/AzLoopType.java | 151 ++++++ .../primitive/AzQueuedAnimation.java | 27 ++ .../property/AzAnimationProperties.java | 91 ++++ .../property/AzAnimationStageProperties.java | 88 ++++ .../codec/AzAnimationPropertiesCodec.java | 54 +++ .../AzAnimationStagePropertiesCodec.java | 66 +++ .../azurelib/rewrite/model/AzBakedModel.java | 53 ++ .../azure/azurelib/rewrite/model/AzBone.java | 459 ++++++++++++++++++ .../rewrite/model/AzBoneMetadata.java | 23 + .../rewrite/model/AzBoneSnapshot.java | 180 +++++++ .../model/cache/AzBakedModelCache.java | 49 ++ .../model/factory/AzBakedModelFactory.java | 147 ++++++ .../impl/AzBuiltinBakedModelFactory.java | 88 ++++ .../model/factory/primitive/VertexSet.java | 120 +++++ .../registry/AzBakedModelFactoryRegistry.java | 38 ++ .../rewrite/render/AzLayerRenderer.java | 52 ++ .../rewrite/render/AzModelRenderer.java | 165 +++++++ .../rewrite/render/AzPhasedRenderer.java | 29 ++ .../azurelib/rewrite/render/AzProvider.java | 78 +++ .../rewrite/render/AzRendererConfig.java | 186 +++++++ .../rewrite/render/AzRendererPipeline.java | 189 ++++++++ .../render/AzRendererPipelineContext.java | 204 ++++++++ .../rewrite/render/armor/AzArmorModel.java | 77 +++ .../render/armor/AzArmorModelRenderer.java | 79 +++ .../rewrite/render/armor/AzArmorRenderer.java | 80 +++ .../render/armor/AzArmorRendererConfig.java | 105 ++++ .../render/armor/AzArmorRendererPipeline.java | 126 +++++ .../armor/AzArmorRendererPipelineContext.java | 78 +++ .../render/armor/AzArmorRendererRegistry.java | 34 ++ .../render/armor/bone/AzArmorBoneContext.java | 197 ++++++++ .../armor/bone/AzArmorBoneProvider.java | 96 ++++ .../bone/AzDefaultArmorBoneProvider.java | 48 ++ .../block/AzBlockEntityModelRenderer.java | 155 ++++++ .../render/block/AzBlockEntityRenderer.java | 64 +++ .../block/AzBlockEntityRendererConfig.java | 92 ++++ .../block/AzBlockEntityRendererPipeline.java | 86 ++++ .../AzBlockEntityRendererPipelineContext.java | 34 ++ .../render/entity/AzEntityLayerRenderer.java | 34 ++ .../entity/AzEntityLeashRenderUtil.java | 159 ++++++ .../render/entity/AzEntityModelRenderer.java | 275 +++++++++++ .../render/entity/AzEntityNameRenderUtil.java | 60 +++ .../render/entity/AzEntityRenderer.java | 123 +++++ .../render/entity/AzEntityRendererConfig.java | 119 +++++ .../entity/AzEntityRendererPipeline.java | 113 +++++ .../AzEntityRendererPipelineContext.java | 53 ++ .../render/item/AzItemGuiRenderUtil.java | 56 +++ .../render/item/AzItemModelRenderer.java | 77 +++ .../rewrite/render/item/AzItemRenderer.java | 96 ++++ .../render/item/AzItemRendererConfig.java | 128 +++++ .../render/item/AzItemRendererPipeline.java | 90 ++++ .../item/AzItemRendererPipelineContext.java | 35 ++ .../render/item/AzItemRendererRegistry.java | 39 ++ .../rewrite/render/layer/AzArmorLayer.java | 414 ++++++++++++++++ .../render/layer/AzAutoGlowingLayer.java | 55 +++ .../render/layer/AzBlockAndItemLayer.java | 179 +++++++ .../rewrite/render/layer/AzRenderLayer.java | 41 ++ .../azurelib/rewrite/util/MoveAnalysis.java | 123 +++++ .../azurelib/rewrite/util/state/State.java | 21 + .../rewrite/util/state/StateMachine.java | 40 ++ .../util/state/StateMachineContext.java | 14 + .../mod/azure/azurelib/util/AzureLibUtil.java | 11 + .../mod/azure/azurelib/util/RenderUtils.java | 23 +- fabric/build.gradle | 1 + .../resources/azurelib.fabric.mixins.json | 4 + gradle.properties | 2 +- neoforge/build.gradle | 4 + .../mixins/NeoMixinHumanoidArmorLayer.java | 92 ++++ .../main/resources/azurelib.forge.mixins.json | 4 + 213 files changed, 12352 insertions(+), 70 deletions(-) create mode 100644 common/src/main/java/mod/azure/azurelib/mixins/BlockEntityMixin_AzBlockEntityAnimatorCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/mixins/EntityMixin_AzEntityAnimatorCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java delete mode 100644 common/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java create mode 100644 common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java create mode 100644 common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java create mode 100644 common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/AzResourceCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationContext.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationTimer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorAccessor.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorConfig.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzBoneAnimationUpdateUtil.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzCachedBoneUpdateUtil.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBakedAnimationCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBoneCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentityRegistry.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAbstractAnimationController.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationController.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerBuilder.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerContainer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerTimer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationQueue.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneAnimationQueueCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneSnapshotCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAbstractKeyframeExecutor.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAnimationPoint.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimation.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimationQueue.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframe.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbackHandler.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbacks.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeExecutor.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeLocation.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeManager.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeStack.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeTransitioner.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzCustomKeyframeHandler.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzParticleKeyframeHandler.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzSoundKeyframeHandler.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/AzAnimationState.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPauseState.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPlayState.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationStopState.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationTransitionState.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/machine/AzAnimationControllerStateMachine.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommandBuilder.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzRootCommandBuilder.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequenceBuilder.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeLoader.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeRegistry.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypes.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingUtil.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzCustomInstructionKeyframeEvent.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzKeyframeEvent.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzParticleKeyframeEvent.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzSoundKeyframeEvent.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzBlockAnimator.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzEntityAnimator.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzItemAnimator.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzBakedAnimationsAdapter.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzKeyframesAdapter.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehavior.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviorRegistry.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviors.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimation.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimations.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzKeyframes.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzLoopType.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzQueuedAnimation.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationProperties.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationStageProperties.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/AzBakedModel.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/AzBone.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneMetadata.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneSnapshot.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/cache/AzBakedModelCache.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/factory/AzBakedModelFactory.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/factory/impl/AzBuiltinBakedModelFactory.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/factory/primitive/VertexSet.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/factory/registry/AzBakedModelFactoryRegistry.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzLayerRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzModelRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzPhasedRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzProvider.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipeline.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModel.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModelRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipelineContext.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererRegistry.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneContext.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneProvider.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzDefaultArmorBoneProvider.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityModelRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipelineContext.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLayerRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLeashRenderUtil.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityModelRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityNameRenderUtil.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipelineContext.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemGuiRenderUtil.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemModelRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipelineContext.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererRegistry.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzArmorLayer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzAutoGlowingLayer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzBlockAndItemLayer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzRenderLayer.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/util/MoveAnalysis.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/util/state/State.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachine.java create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachineContext.java create mode 100644 neoforge/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java diff --git a/common/build.gradle b/common/build.gradle index 8216986b1..25080ca95 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -17,6 +17,8 @@ minecraft { dependencies { compileOnly group:'org.spongepowered', name:'mixin', version:'0.8.5' + compileOnly group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.3.5' + annotationProcessor group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.3.5' } publishing { diff --git a/common/src/main/java/mod/azure/azurelib/animatable/GeoBlockEntity.java b/common/src/main/java/mod/azure/azurelib/animatable/GeoBlockEntity.java index 9257e1f1a..a40677591 100644 --- a/common/src/main/java/mod/azure/azurelib/animatable/GeoBlockEntity.java +++ b/common/src/main/java/mod/azure/azurelib/animatable/GeoBlockEntity.java @@ -25,6 +25,7 @@ /** * The {@link GeoAnimatable} interface specific to {@link BlockEntity BlockEntities} */ +@Deprecated(forRemoval = true) public interface GeoBlockEntity extends GeoAnimatable { /** * Get server-synced animation data via its relevant {@link SerializableDataTicket}.
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/GeoEntity.java b/common/src/main/java/mod/azure/azurelib/animatable/GeoEntity.java index b11ded72e..0f6415a20 100644 --- a/common/src/main/java/mod/azure/azurelib/animatable/GeoEntity.java +++ b/common/src/main/java/mod/azure/azurelib/animatable/GeoEntity.java @@ -22,6 +22,7 @@ * The {@link GeoAnimatable} interface specific to {@link net.minecraft.world.entity.Entity Entities}. This also applies to Projectiles and other Entity subclasses.
* NOTE: This cannot be used for entities using the {@link mod.azure.azurelib.renderer.GeoReplacedEntityRenderer} as you aren't extending {@code Entity}. Use {@link GeoReplacedEntity} instead. */ +@Deprecated(forRemoval = true) public interface GeoEntity extends GeoAnimatable { /** * Get server-synced animation data via its relevant {@link SerializableDataTicket}.
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/GeoItem.java b/common/src/main/java/mod/azure/azurelib/animatable/GeoItem.java index 48bb23e8d..fd498bee5 100644 --- a/common/src/main/java/mod/azure/azurelib/animatable/GeoItem.java +++ b/common/src/main/java/mod/azure/azurelib/animatable/GeoItem.java @@ -34,6 +34,7 @@ /** * The {@link mod.azure.azurelib.core.animatable.GeoAnimatable GeoAnimatable} interface specific to {@link net.minecraft.world.item.Item Items}. This also applies to armor, as they are just items too. */ +@Deprecated(forRemoval = true) public interface GeoItem extends SingletonGeoAnimatable { String ID_NBT_KEY = "AzureLibID"; diff --git a/common/src/main/java/mod/azure/azurelib/animatable/GeoReplacedEntity.java b/common/src/main/java/mod/azure/azurelib/animatable/GeoReplacedEntity.java index 0295db2ef..cf53ce87f 100644 --- a/common/src/main/java/mod/azure/azurelib/animatable/GeoReplacedEntity.java +++ b/common/src/main/java/mod/azure/azurelib/animatable/GeoReplacedEntity.java @@ -24,6 +24,7 @@ /** * The {@link GeoAnimatable} interface specific to {@link Entity Entities}. This interface is specifically for entities replacing the rendering of other, existing entities. */ +@Deprecated(forRemoval = true) public interface GeoReplacedEntity extends SingletonGeoAnimatable { /** * Returns the {@link EntityType} this entity is intending to replace.
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/SingletonGeoAnimatable.java b/common/src/main/java/mod/azure/azurelib/animatable/SingletonGeoAnimatable.java index d5453b5be..e0dd918e0 100644 --- a/common/src/main/java/mod/azure/azurelib/animatable/SingletonGeoAnimatable.java +++ b/common/src/main/java/mod/azure/azurelib/animatable/SingletonGeoAnimatable.java @@ -26,6 +26,7 @@ /** * The {@link GeoAnimatable} interface specific to singleton objects. This primarily applies to armor and items */ +@Deprecated(forRemoval = true) public interface SingletonGeoAnimatable extends GeoAnimatable { /** * Register this as a synched {@code GeoAnimatable} instance with AzureLib's networking functions.
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/client/RenderProvider.java b/common/src/main/java/mod/azure/azurelib/animatable/client/RenderProvider.java index fa96652fd..e82f1769c 100644 --- a/common/src/main/java/mod/azure/azurelib/animatable/client/RenderProvider.java +++ b/common/src/main/java/mod/azure/azurelib/animatable/client/RenderProvider.java @@ -15,6 +15,7 @@ * Internal interface for safely providing a custom renderer instances at runtime.
* This can be safely instantiated as a new anonymous class inside your {@link Item} class */ +@Deprecated(forRemoval = true) public interface RenderProvider { RenderProvider DEFAULT = new RenderProvider() {}; diff --git a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java index 600307242..6d45e7adc 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java +++ b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java @@ -12,6 +12,8 @@ import mod.azure.azurelib.loading.object.BakedAnimations; import mod.azure.azurelib.loading.object.BakedModelFactory; import mod.azure.azurelib.loading.object.GeometryTree; +import mod.azure.azurelib.rewrite.animation.cache.AzBakedAnimationCache; +import mod.azure.azurelib.rewrite.model.cache.AzBakedModelCache; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.PreparableReloadListener.PreparationBarrier; @@ -72,9 +74,16 @@ public static CompletableFuture reload(PreparationBarrier stage, ResourceM Map models = new Object2ObjectOpenHashMap<>(); return CompletableFuture - .allOf(loadAnimations(backgroundExecutor, resourceManager, animations::put), - loadModels(backgroundExecutor, resourceManager, models::put)) - .thenCompose(stage::wait).thenAcceptAsync(empty -> { + .allOf( + // TODO: Remove these. + loadAnimations(backgroundExecutor, resourceManager, animations::put), + loadModels(backgroundExecutor, resourceManager, models::put), + // Forward-support for new cache components + AzBakedAnimationCache.getInstance().loadAnimations(backgroundExecutor, resourceManager), + AzBakedModelCache.getInstance().loadModels(backgroundExecutor, resourceManager) + ) + .thenCompose(stage::wait) + .thenAcceptAsync(empty -> { AzureLibCache.ANIMATIONS = animations; AzureLibCache.MODELS = models; }, gameExecutor); diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/BakedGeoModel.java b/common/src/main/java/mod/azure/azurelib/cache/object/BakedGeoModel.java index c5dfce06d..e800c2021 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/object/BakedGeoModel.java +++ b/common/src/main/java/mod/azure/azurelib/cache/object/BakedGeoModel.java @@ -17,6 +17,7 @@ /** * Baked model object for AzureLib models. */ +@Deprecated(forRemoval = true) public record BakedGeoModel(List topLevelBones, ModelProperties properties) implements CoreBakedGeoModel { /** * Gets the list of top-level bones for this model. diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoBone.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoBone.java index 245ae593b..c8b4c5f8e 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoBone.java +++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoBone.java @@ -24,6 +24,7 @@ * Mutable bone object representing a set of cubes, as well as child bones.
* This is the object that is directly modified by animations to handle movement */ +@Deprecated(forRemoval = true) public class GeoBone implements CoreGeoBone { private final GeoBone parent; private final String name; diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java index c2f36f8ad..11d2139bc 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java +++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java @@ -12,4 +12,5 @@ /** * Baked cuboid for a {@link GeoBone} */ +@Deprecated(forRemoval = true) public record GeoCube(GeoQuad[] quads, Vec3 pivot, Vec3 rotation, Vec3 size, double inflate, boolean mirror) {} diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java index a35821092..4758c7483 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java +++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java @@ -15,6 +15,7 @@ /** * Quad data holder */ +@Deprecated(forRemoval = true) public record GeoQuad( GeoVertex[] vertices, Vector3f normal, diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java index aad4c474d..0c4ba1732 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java +++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java @@ -15,6 +15,7 @@ * @param texU The texture U coordinate * @param texV The texture V coordinate */ +@Deprecated(forRemoval = true) public record GeoVertex(Vector3f position, float texU, float texV) { public GeoVertex(double x, double y, double z) { this(new Vector3f((float)x, (float)y, (float)z), 0, 0); diff --git a/common/src/main/java/mod/azure/azurelib/cache/texture/GeoAbstractTexture.java b/common/src/main/java/mod/azure/azurelib/cache/texture/GeoAbstractTexture.java index 28605b174..c7a43845f 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/texture/GeoAbstractTexture.java +++ b/common/src/main/java/mod/azure/azurelib/cache/texture/GeoAbstractTexture.java @@ -30,6 +30,7 @@ * Abstract texture wrapper for AzureLib textures.
* Mostly just handles boilerplate */ +@Deprecated(forRemoval = true) public abstract class GeoAbstractTexture extends AbstractTexture { /** * Generates the texture instance for the given path with the given appendix if it hasn't already been generated diff --git a/common/src/main/java/mod/azure/azurelib/constant/DataTickets.java b/common/src/main/java/mod/azure/azurelib/constant/DataTickets.java index 85b098c10..f2d472c19 100644 --- a/common/src/main/java/mod/azure/azurelib/constant/DataTickets.java +++ b/common/src/main/java/mod/azure/azurelib/constant/DataTickets.java @@ -29,6 +29,7 @@ * Stores the default (builtin) {@link DataTicket DataTickets} used in AzureLib.
* Additionally handles registration of {@link mod.azure.azurelib.network.SerializableDataTicket SerializableDataTickets} */ +@Deprecated(forRemoval = true) public final class DataTickets { private static final Map> SERIALIZABLE_TICKETS = new ConcurrentHashMap<>(); diff --git a/common/src/main/java/mod/azure/azurelib/constant/DefaultAnimations.java b/common/src/main/java/mod/azure/azurelib/constant/DefaultAnimations.java index ea56e7697..d78684faf 100644 --- a/common/src/main/java/mod/azure/azurelib/constant/DefaultAnimations.java +++ b/common/src/main/java/mod/azure/azurelib/constant/DefaultAnimations.java @@ -24,6 +24,7 @@ * Using these won't affect much, but it may help keep some consistency in animation namings.
* Additionally, it encourages use of cached {@link mod.azure.azurelib.core.animation.RawAnimation RawAnimations}, to reduce overheads. */ +@Deprecated(forRemoval = true) public final class DefaultAnimations { public static final RawAnimation ITEM_ON_USE = RawAnimation.begin().thenPlay("item.use"); diff --git a/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java b/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java index 73671f50a..e4b4936ea 100644 --- a/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java +++ b/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java @@ -13,6 +13,8 @@ import mod.azure.azurelib.cache.object.BakedGeoModel; import mod.azure.azurelib.loading.json.raw.Model; import mod.azure.azurelib.loading.object.BakedAnimations; +import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimation; +import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimations; import mod.azure.azurelib.util.JsonUtil; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; @@ -32,16 +34,28 @@ public final class FileLoader { * @param location The resource path of the animations file * @param manager The Minecraft {@code ResourceManager} responsible for maintaining in-memory resource access */ + @Deprecated(forRemoval = true) public static BakedAnimations loadAnimationsFile(ResourceLocation location, ResourceManager manager) { return JsonUtil.GEO_GSON.fromJson(loadFile(location, manager), BakedAnimations.class); } + /** + * Load up and deserialize an animation json file to its respective {@link AzBakedAnimation} components + * + * @param location The resource path of the animations file + * @param manager The Minecraft {@code ResourceManager} responsible for maintaining in-memory resource access + */ + public static AzBakedAnimations loadAzAnimationsFile(ResourceLocation location, ResourceManager manager) { + return JsonUtil.GEO_GSON.fromJson(loadFile(location, manager), AzBakedAnimations.class); + } + /** * Load up and deserialize a geo model json file to its respective {@link BakedGeoModel} format * * @param location The resource path of the model file * @param manager The Minecraft {@code ResourceManager} responsible for maintaining in-memory resource access */ + @Deprecated(forRemoval = true) public static Model loadModelFile(ResourceLocation location, ResourceManager manager) { return JsonUtil.GEO_GSON.fromJson(loadFile(location, manager), Model.class); } diff --git a/common/src/main/java/mod/azure/azurelib/loading/object/BakedModelFactory.java b/common/src/main/java/mod/azure/azurelib/loading/object/BakedModelFactory.java index 5e4a2fce2..d34d717f8 100644 --- a/common/src/main/java/mod/azure/azurelib/loading/object/BakedModelFactory.java +++ b/common/src/main/java/mod/azure/azurelib/loading/object/BakedModelFactory.java @@ -33,6 +33,7 @@ * Base interface for a factory of {@link BakedGeoModel} objects. * Handled by default by AzureLib, but custom implementations may be added by other mods for special needs */ +@Deprecated(forRemoval = true) public interface BakedModelFactory { final Map FACTORIES = new Object2ObjectOpenHashMap<>(1); final BakedModelFactory DEFAULT_FACTORY = new Builtin(); diff --git a/common/src/main/java/mod/azure/azurelib/mixins/BlockEntityMixin_AzBlockEntityAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/mixins/BlockEntityMixin_AzBlockEntityAnimatorCache.java new file mode 100644 index 000000000..3de316e31 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/mixins/BlockEntityMixin_AzBlockEntityAnimatorCache.java @@ -0,0 +1,32 @@ +package mod.azure.azurelib.mixins; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +/** + * Mixin class that implements the {@code AzAnimatorAccessor} interface to enable managing and associating + * an {@link AzAnimator} instance with a {@link BlockEntity}. This allows for caching and retrieval of the animator + * associated with specific block entities. This mixin modifies the behavior of {@link BlockEntity} by adding an + * animator cache that can be used to store and retrieve {@link AzAnimator} instances for animation handling. + */ +@Mixin(BlockEntity.class) +public abstract class BlockEntityMixin_AzBlockEntityAnimatorCache implements AzAnimatorAccessor { + + @Unique + @Nullable + private AzAnimator animator; + + @Override + public void setAnimator(@Nullable AzAnimator animator) { + this.animator = animator; + } + + @Override + public @Nullable AzAnimator getAnimatorOrNull() { + return animator; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/mixins/EntityMixin_AzEntityAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/mixins/EntityMixin_AzEntityAnimatorCache.java new file mode 100644 index 000000000..17a9c45b5 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/mixins/EntityMixin_AzEntityAnimatorCache.java @@ -0,0 +1,32 @@ +package mod.azure.azurelib.mixins; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +/** + * A Mixin class designed to integrate an animation cache mechanism into the {@link Entity} class through the use of the + * {@link AzAnimatorAccessor} interface. This allows entities to store an instance of {@link AzAnimator} for managing + * animations. Implements methods to set and retrieve the {@link AzAnimator} instance, enabling animation control and + * association to the entity. + */ +@Mixin(Entity.class) +public abstract class EntityMixin_AzEntityAnimatorCache implements AzAnimatorAccessor { + + @Unique + @Nullable + private AzAnimator animator; + + @Override + public void setAnimator(@Nullable AzAnimator animator) { + this.animator = animator; + } + + @Override + public @Nullable AzAnimator getAnimatorOrNull() { + return animator; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java b/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java index fee1e43f4..abd9f2f6c 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java @@ -1,19 +1,23 @@ package mod.azure.azurelib.mixins; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; import com.mojang.blaze3d.vertex.PoseStack; import mod.azure.azurelib.animatable.GeoItem; import mod.azure.azurelib.animatable.client.RenderProvider; +import mod.azure.azurelib.renderer.GeoArmorRenderer; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; +import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; -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.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** @@ -21,21 +25,67 @@ */ @Mixin(value = HumanoidArmorLayer.class, priority = 700) public abstract class FabricMixinHumanoidArmorLayer> { - @Unique - private LivingEntity gl_storedEntity; - @Unique - private EquipmentSlot gl_storedSlot; - @Unique - private ItemStack gl_storedItemStack; - - @Inject(method = "renderArmorPiece", at = @At(value = "HEAD")) - public void armorModelHook(PoseStack poseStack, MultiBufferSource multiBufferSource, T livingEntity, EquipmentSlot equipmentSlot, int i, A humanoidModel, CallbackInfo ci) { - this.gl_storedEntity = livingEntity; - this.gl_storedSlot = equipmentSlot; - this.gl_storedItemStack = livingEntity.getItemBySlot(equipmentSlot); + @ModifyExpressionValue( + method = "renderArmorPiece", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;getItemBySlot(Lnet/minecraft/world/entity/EquipmentSlot;)Lnet/minecraft/world/item/ItemStack;" + ) + ) + private ItemStack azurelib$captureItemBySlot( + ItemStack original, + @Share("item_by_slot") LocalRef itemBySlotRef + ) { + itemBySlotRef.set(original); + return original; } - @ModifyArg(method = "renderArmorPiece", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;renderModel(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/HumanoidModel;ZFFFLjava/lang/String;)V"), index = 4) - public A injectArmor(A humanoidModel) { - return this.gl_storedItemStack.getItem() instanceof GeoItem ? (A) RenderProvider.of(this.gl_storedItemStack).getGenericArmorModel(this.gl_storedEntity, this.gl_storedItemStack, this.gl_storedSlot, (HumanoidModel) humanoidModel) : humanoidModel; } + @Inject( + method = "renderArmorPiece", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;usesInnerModel(Lnet/minecraft/world/entity/EquipmentSlot;)Z" + ), cancellable = true + ) + public void azurelib$renderAzurelibModel( + PoseStack poseStack, + MultiBufferSource bufferSource, + T entity, + EquipmentSlot equipmentSlot, + int packedLight, + A baseModel, + CallbackInfo ci, + @Share("item_by_slot") LocalRef itemBySlotRef + ) { + var stack = itemBySlotRef.get(); + var renderProvider = RenderProvider.of(stack); + @SuppressWarnings("unchecked") + var humanoidModel = (HumanoidModel) baseModel; + var geckolibModel = renderProvider + .getGenericArmorModel(entity, stack, equipmentSlot, humanoidModel); + + if (geckolibModel != null && stack.getItem() instanceof GeoItem) { + if (geckolibModel instanceof GeoArmorRenderer geoArmorRenderer) { + geoArmorRenderer.prepForRender(entity, stack, equipmentSlot, baseModel); + } + + baseModel.copyPropertiesTo((A) geckolibModel); + + geckolibModel.renderToBuffer(poseStack, null, packedLight, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1); + ci.cancel(); + } + + var renderer = AzArmorRendererRegistry.getOrNull(stack.getItem()); + + if (renderer != null) { + var rendererPipeline = renderer.rendererPipeline(); + var armorModel = rendererPipeline.armorModel(); + @SuppressWarnings("unchecked") + var typedHumanoidModel = (HumanoidModel) armorModel; + + renderer.prepForRender(entity, stack, equipmentSlot, baseModel); + baseModel.copyPropertiesTo(typedHumanoidModel); + armorModel.renderToBuffer(poseStack, null, packedLight, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1); + ci.cancel(); + } + } } diff --git a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java new file mode 100644 index 000000000..e61f5e3c3 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java @@ -0,0 +1,27 @@ +package mod.azure.azurelib.mixins; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor; +import mod.azure.azurelib.rewrite.animation.cache.AzIdentifiableItemStackAnimatorCache; +import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; +import mod.azure.azurelib.util.AzureLibUtil; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ItemStack.class) +public abstract class ItemStackMixin_AzItemAnimatorCache implements AzAnimatorAccessor { + + @Override + public void setAnimator(@Nullable AzAnimator animator) { + var itemStack = AzureLibUtil.self(this); + AzIdentifiableItemStackAnimatorCache.getInstance().add(itemStack, (AzItemAnimator) animator); + } + + @Override + public @Nullable AzAnimator getAnimatorOrNull() { + var self = AzureLibUtil.self(this); + var uuid = self.getOrCreateTag().getUUID("az_id"); + return AzIdentifiableItemStackAnimatorCache.getInstance().getOrNull(uuid); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java new file mode 100644 index 000000000..1295f25e8 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java @@ -0,0 +1,37 @@ +package mod.azure.azurelib.mixins; + +import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; +import mod.azure.azurelib.util.AzureLibUtil; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +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; + +import java.util.UUID; + +/** + * A mixin class for modifying the initialization behavior of the {@link ItemStack} class. This mixin specifically + * ensures that a unique identity component is added to an {@link ItemStack} upon creation, provided that the associated + * item has been registered in the {@link AzIdentityRegistry}. When an {@link ItemStack} is instantiated, the mixin + * checks if: - The item has an identity registered in {@link AzIdentityRegistry}. - The provided + * {@link PatchedDataComponentMap} does not already contain an `az_id` component. If both conditions are met, the mixin + * assigns a universally unique identifier (UUID) as the `az_id` component to the associated + * {@link PatchedDataComponentMap}. This mechanism enables unique identification and tracking of specific item stacks in + * the game. + */ +@Mixin(ItemStack.class) +public class ItemStackMixin_AzItemStackIdentityRegistry { + + @Inject( + method = "(Lnet/minecraft/nbt/CompoundTag;)V", + at = @At("TAIL") + ) + public void az_addIdentityComponent(CompoundTag compoundTag, CallbackInfo ci) { + var self = AzureLibUtil.self(this); + if (AzIdentityRegistry.hasIdentity(self.getItem()) && !compoundTag.hasUUID("az_id")) { + compoundTag.putUUID("az_id", UUID.randomUUID()); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java b/common/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java deleted file mode 100644 index fb5ade0ed..000000000 --- a/common/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java +++ /dev/null @@ -1,44 +0,0 @@ -package mod.azure.azurelib.mixins; - - -import com.mojang.blaze3d.vertex.PoseStack; -import mod.azure.azurelib.animatable.GeoItem; -import mod.azure.azurelib.animatable.client.RenderProvider; -import net.minecraft.client.model.HumanoidModel; -import net.minecraft.client.model.Model; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.ItemStack; -import org.spongepowered.asm.mixin.Mixin; -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.ModifyArg; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -/** - * Render hook for injecting AzureLib's armor rendering functionalities - */ -@Mixin(value = HumanoidArmorLayer.class, priority = 700) -public class NeoMixinHumanoidArmorLayer> { - - @Unique - private LivingEntity gl_storedEntity; - @Unique - private EquipmentSlot gl_storedSlot; - @Unique - private ItemStack gl_storedItemStack; - - @Inject(method = "renderArmorPiece", at = @At(value = "HEAD")) - public void armorModelHook(PoseStack poseStack, MultiBufferSource source, T livingEntity, EquipmentSlot equipmentSlot, int i, A model, CallbackInfo ci) { - this.gl_storedEntity = livingEntity; - this.gl_storedSlot = equipmentSlot; - this.gl_storedItemStack = livingEntity.getItemBySlot(equipmentSlot); - } - - @ModifyArg(method = "renderArmorPiece", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;renderModel(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/Model;ZFFFLnet/minecraft/resources/ResourceLocation;)V", remap = false), index = 4) - public Model injectArmor(Model humanoidModel) { - return this.gl_storedItemStack.getItem() instanceof GeoItem ? (A) RenderProvider.of(this.gl_storedItemStack).getGenericArmorModel(this.gl_storedEntity, this.gl_storedItemStack, this.gl_storedSlot, (HumanoidModel) humanoidModel) : humanoidModel; } -} \ No newline at end of file diff --git a/common/src/main/java/mod/azure/azurelib/model/DefaultedBlockGeoModel.java b/common/src/main/java/mod/azure/azurelib/model/DefaultedBlockGeoModel.java index a26ed806a..7f51b6d9f 100644 --- a/common/src/main/java/mod/azure/azurelib/model/DefaultedBlockGeoModel.java +++ b/common/src/main/java/mod/azure/azurelib/model/DefaultedBlockGeoModel.java @@ -14,6 +14,7 @@ * {@link DefaultedGeoModel} specific to {@link net.minecraft.world.level.block.Block Blocks}. * Using this class pre-sorts provided asset paths into the "block" subdirectory */ +@Deprecated(forRemoval = true) public class DefaultedBlockGeoModel extends DefaultedGeoModel { /** * Create a new instance of this model class.
diff --git a/common/src/main/java/mod/azure/azurelib/model/DefaultedEntityGeoModel.java b/common/src/main/java/mod/azure/azurelib/model/DefaultedEntityGeoModel.java index 0b544435c..e5b0e0445 100644 --- a/common/src/main/java/mod/azure/azurelib/model/DefaultedEntityGeoModel.java +++ b/common/src/main/java/mod/azure/azurelib/model/DefaultedEntityGeoModel.java @@ -20,6 +20,7 @@ * Using this class pre-sorts provided asset paths into the "entity" subdirectory * Additionally it can automatically handle head-turning if the entity has a "head" bone */ +@Deprecated(forRemoval = true) public class DefaultedEntityGeoModel extends DefaultedGeoModel { private final boolean turnsHead; diff --git a/common/src/main/java/mod/azure/azurelib/model/DefaultedGeoModel.java b/common/src/main/java/mod/azure/azurelib/model/DefaultedGeoModel.java index 6b49f3eed..4924b9cb6 100644 --- a/common/src/main/java/mod/azure/azurelib/model/DefaultedGeoModel.java +++ b/common/src/main/java/mod/azure/azurelib/model/DefaultedGeoModel.java @@ -15,6 +15,7 @@ * This class allows for minimal boilerplate when implementing basic models, and saves on new classes.
* Additionally, it encourages consistency and sorting of asset paths. */ +@Deprecated(forRemoval = true) public abstract class DefaultedGeoModel extends GeoModel { private ResourceLocation modelPath; private ResourceLocation texturePath; diff --git a/common/src/main/java/mod/azure/azurelib/model/DefaultedItemGeoModel.java b/common/src/main/java/mod/azure/azurelib/model/DefaultedItemGeoModel.java index 87b3069b7..5efc325fc 100644 --- a/common/src/main/java/mod/azure/azurelib/model/DefaultedItemGeoModel.java +++ b/common/src/main/java/mod/azure/azurelib/model/DefaultedItemGeoModel.java @@ -14,6 +14,7 @@ * {@link DefaultedGeoModel} specific to {@link net.minecraft.world.item.Item Items}. * Using this class pre-sorts provided asset paths into the "item" subdirectory */ +@Deprecated(forRemoval = true) public class DefaultedItemGeoModel extends DefaultedGeoModel { /** * Create a new instance of this model class.
diff --git a/common/src/main/java/mod/azure/azurelib/model/GeoModel.java b/common/src/main/java/mod/azure/azurelib/model/GeoModel.java index 97ec86927..af7c2aa7b 100644 --- a/common/src/main/java/mod/azure/azurelib/model/GeoModel.java +++ b/common/src/main/java/mod/azure/azurelib/model/GeoModel.java @@ -39,6 +39,7 @@ * Base class for all code-based model objects.
* All models to registered to a {@link GeoRenderer} should be an instance of this or one of its subclasses. */ +@Deprecated(forRemoval = true) public abstract class GeoModel implements CoreGeoModel { private final AnimationProcessor processor = new AnimationProcessor<>(this); diff --git a/common/src/main/java/mod/azure/azurelib/model/data/EntityModelData.java b/common/src/main/java/mod/azure/azurelib/model/data/EntityModelData.java index a55600e3d..50e30294c 100644 --- a/common/src/main/java/mod/azure/azurelib/model/data/EntityModelData.java +++ b/common/src/main/java/mod/azure/azurelib/model/data/EntityModelData.java @@ -10,4 +10,5 @@ /** * Container class for various pieces of data relating to a model's current state. */ +@Deprecated(forRemoval = true) public record EntityModelData(boolean isSitting, boolean isChild, float netHeadYaw, float headPitch) {} diff --git a/common/src/main/java/mod/azure/azurelib/network/SerializableDataTicket.java b/common/src/main/java/mod/azure/azurelib/network/SerializableDataTicket.java index 50e6465d0..c3b159fa7 100644 --- a/common/src/main/java/mod/azure/azurelib/network/SerializableDataTicket.java +++ b/common/src/main/java/mod/azure/azurelib/network/SerializableDataTicket.java @@ -15,6 +15,7 @@ * Network-compatible {@link mod.azure.azurelib.core.object.DataTicket} implementation. * Used for sending data from server -> client in an easy manner */ +@Deprecated(forRemoval = true) public abstract class SerializableDataTicket extends DataTicket { protected SerializableDataTicket(String id, Class objectType) { super(id, objectType); diff --git a/common/src/main/java/mod/azure/azurelib/network/api/IPacket.java b/common/src/main/java/mod/azure/azurelib/network/api/IPacket.java index ced8116e5..05e04b940 100644 --- a/common/src/main/java/mod/azure/azurelib/network/api/IPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/api/IPacket.java @@ -14,6 +14,7 @@ import net.minecraft.resources.ResourceLocation; +@Deprecated(forRemoval = true) public interface IPacket { ResourceLocation getPacketId(); diff --git a/common/src/main/java/mod/azure/azurelib/network/api/IPacketDecoder.java b/common/src/main/java/mod/azure/azurelib/network/api/IPacketDecoder.java index cd89125d5..20a8ebe00 100644 --- a/common/src/main/java/mod/azure/azurelib/network/api/IPacketDecoder.java +++ b/common/src/main/java/mod/azure/azurelib/network/api/IPacketDecoder.java @@ -15,6 +15,7 @@ import net.minecraft.network.FriendlyByteBuf; @FunctionalInterface +@Deprecated(forRemoval = true) public interface IPacketDecoder { T decode(FriendlyByteBuf buffer); diff --git a/common/src/main/java/mod/azure/azurelib/network/api/IPacketEncoder.java b/common/src/main/java/mod/azure/azurelib/network/api/IPacketEncoder.java index b2b0296fc..f87f1fccd 100644 --- a/common/src/main/java/mod/azure/azurelib/network/api/IPacketEncoder.java +++ b/common/src/main/java/mod/azure/azurelib/network/api/IPacketEncoder.java @@ -15,6 +15,7 @@ import net.minecraft.network.FriendlyByteBuf; @FunctionalInterface +@Deprecated(forRemoval = true) public interface IPacketEncoder { void encode(T data, FriendlyByteBuf buffer); diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AnimDataSyncPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AnimDataSyncPacket.java index 0f1070d88..11a1fbea2 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/AnimDataSyncPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AnimDataSyncPacket.java @@ -16,6 +16,7 @@ * Packet for syncing user-definable animation data for * {@link SingletonGeoAnimatable} instances */ +@Deprecated(forRemoval = true) public class AnimDataSyncPacket extends AbstractPacket { private final String syncableId; private final long instanceId; diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AnimTriggerPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AnimTriggerPacket.java index 4c62c67f6..31eabc6fe 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/AnimTriggerPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AnimTriggerPacket.java @@ -12,6 +12,7 @@ * Packet for syncing user-definable animations that can be triggered from the * server */ +@Deprecated(forRemoval = true) public class AnimTriggerPacket extends AbstractPacket { private final String syncableId; private final long instanceId; diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java new file mode 100644 index 000000000..a06daf2fb --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java @@ -0,0 +1,74 @@ +package mod.azure.azurelib.network.packet; + +import mod.azure.azurelib.network.AbstractPacket; +import mod.azure.azurelib.platform.services.AzureLibNetwork; +import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; +import mod.azure.azurelib.util.ClientUtils; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +public class AzBlockEntityDispatchCommandPacket extends AbstractPacket { + + private final BlockPos blockPos; + private final AzCommand dispatchCommand; + + public static final StreamCodec CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, + AzBlockEntityDispatchCommandPacket::blockPos, + AzCommand.CODEC, + AzBlockEntityDispatchCommandPacket::dispatchCommand, + AzBlockEntityDispatchCommandPacket::new + ); + + public AzBlockEntityDispatchCommandPacket(BlockPos blockPos, AzCommand dispatchCommand) { + this.blockPos = blockPos; + this.dispatchCommand = dispatchCommand; + } + + public BlockPos blockPos() { + return blockPos; + } + + public AzCommand dispatchCommand() { + return dispatchCommand; + } + + @Override + public void encode(FriendlyByteBuf buf) { + buf.writeBlockPos(this.blockPos); + AzCommand.encode(buf, dispatchCommand()); + } + + public static AzBlockEntityDispatchCommandPacket decode(FriendlyByteBuf buf) { + var blockPos = buf.readBlockPos(); + + // Deserialize the AzCommand + AzCommand command = AzCommand.decode(buf); + + return new AzBlockEntityDispatchCommandPacket(blockPos, dispatchCommand()); + } + + + @Override + public void handle() { + var blockEntity = ClientUtils.getLevel().getBlockEntity(blockPos); + + if (blockEntity == null) { + return; + } + + var animator = AzAnimatorAccessor.getOrNull(blockEntity); + + if (animator != null) { + dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator)); + } + } + + @Override + public ResourceLocation getPacketID() { + return AzureLibNetwork.AZ_BLOCKENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java new file mode 100644 index 000000000..1b0a54939 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java @@ -0,0 +1,48 @@ +package mod.azure.azurelib.network.packet; + +import mod.azure.azurelib.network.AbstractPacket; +import mod.azure.azurelib.platform.services.AzureLibNetwork; +import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; +import mod.azure.azurelib.util.ClientUtils; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +public class AzEntityDispatchCommandPacket extends AbstractPacket { + + private final int entityId; + private final AzCommand dispatchCommand; + + public AzEntityDispatchCommandPacket(int entityId, AzCommand dispatchCommand) { + this.entityId = entityId; + this.dispatchCommand = dispatchCommand; + } + + @Override + public void encode(FriendlyByteBuf buf) { + buf.writeInt(this.entityId); + // TODO: Fix this, as I don't believe this is correct. + //buf.writeByte(dispatchCommand.actions().size()); + } + + @Override + public void handle() { + var entity = ClientUtils.getLevel().getEntity(this.entityId); + + if (entity == null) { + return; + } + + var animator = AzAnimatorAccessor.getOrNull(entity); + + if (animator != null) { + dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator)); + } + } + + @Override + public ResourceLocation getPacketID() { + return AzureLibNetwork.AZ_ENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java new file mode 100644 index 000000000..06694054a --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java @@ -0,0 +1,43 @@ +package mod.azure.azurelib.network.packet; + +import mod.azure.azurelib.network.AbstractPacket; +import mod.azure.azurelib.platform.services.AzureLibNetwork; +import mod.azure.azurelib.rewrite.animation.cache.AzIdentifiableItemStackAnimatorCache; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +import java.util.UUID; + +public class AzItemStackDispatchCommandPacket extends AbstractPacket { + + private final UUID itemStackId; + private final AzCommand dispatchCommand; + + public AzItemStackDispatchCommandPacket(UUID itemStackId, AzCommand dispatchCommand) { + this.itemStackId = itemStackId; + this.dispatchCommand = dispatchCommand; + } + + @Override + public void encode(FriendlyByteBuf buf) { + buf.writeUUID(this.itemStackId); + // TODO: Fix this, as I don't believe this is correct. + //buf.writeByte(dispatchCommand.actions().size()); + } + + @Override + public void handle() { + var animator = AzIdentifiableItemStackAnimatorCache.getInstance().getOrNull(itemStackId); + + if (animator != null) { + dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator)); + } + } + + @Override + public ResourceLocation getPacketID() { + return AzureLibNetwork.AZ_ITEM_STACK_DISPATCH_COMMAND_SYNC_PACKET_ID; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimDataSyncPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimDataSyncPacket.java index 4d4d69f5d..effa38cb0 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimDataSyncPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimDataSyncPacket.java @@ -17,6 +17,7 @@ * Packet for syncing user-definable animation data for {@link BlockEntity * BlockEntities} */ +@Deprecated(forRemoval = true) public class BlockEntityAnimDataSyncPacket extends AbstractPacket { private final BlockPos blockPos; private final SerializableDataTicket dataTicket; diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimTriggerPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimTriggerPacket.java index ad3992d7e..b6caa4fa8 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimTriggerPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimTriggerPacket.java @@ -23,6 +23,7 @@ * server for {@link net.minecraft.world.level.block.entity.BlockEntity * BlockEntities} */ +@Deprecated(forRemoval = true) public class BlockEntityAnimTriggerPacket extends AbstractPacket { private final BlockPos blockPos; private final String controllerName; diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimDataSyncPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimDataSyncPacket.java index f523bf6cc..dcbdba4ad 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimDataSyncPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimDataSyncPacket.java @@ -14,6 +14,7 @@ * Packet for syncing user-definable animation data for * {@link net.minecraft.world.entity.Entity Entities} */ +@Deprecated(forRemoval = true) public class EntityAnimDataSyncPacket extends AbstractPacket { private final int entityId; private final SerializableDataTicket dataTicket; diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimTriggerPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimTriggerPacket.java index 7c80cc8e4..38fdcf7ba 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimTriggerPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimTriggerPacket.java @@ -23,6 +23,7 @@ * Packet for syncing user-definable animations that can be triggered from the * server for {@link net.minecraft.world.entity.Entity Entities} */ +@Deprecated(forRemoval = true) public class EntityAnimTriggerPacket extends AbstractPacket { private final int entityId; private final boolean isReplacedEntity; diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacket.java index add06ae69..133bbd26c 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacket.java @@ -5,6 +5,7 @@ import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.world.entity.Entity; +@Deprecated(forRemoval = true) public class EntityPacket { public static Packet createPacket(Entity entity) { return Services.NETWORK.createPacket(entity); diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacketOnClient.java b/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacketOnClient.java index f6d0aca0f..fedab15f2 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacketOnClient.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacketOnClient.java @@ -9,6 +9,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +@Deprecated(forRemoval = true) public class EntityPacketOnClient { public static void onPacket(Minecraft context, FriendlyByteBuf byteBuf) { EntityType type = BuiltInRegistries.ENTITY_TYPE.byId(byteBuf.readVarInt()); diff --git a/common/src/main/java/mod/azure/azurelib/platform/services/AzureLibNetwork.java b/common/src/main/java/mod/azure/azurelib/platform/services/AzureLibNetwork.java index 981da9281..725978cce 100644 --- a/common/src/main/java/mod/azure/azurelib/platform/services/AzureLibNetwork.java +++ b/common/src/main/java/mod/azure/azurelib/platform/services/AzureLibNetwork.java @@ -20,23 +20,46 @@ class LockHolder { // Package private class } public interface AzureLibNetwork { + @Deprecated(forRemoval = true) ResourceLocation ANIM_DATA_SYNC_PACKET_ID = AzureLib.modResource("anim_data_sync"); + @Deprecated(forRemoval = true) ResourceLocation ANIM_TRIGGER_SYNC_PACKET_ID = AzureLib.modResource("anim_trigger_sync"); + @Deprecated(forRemoval = true) ResourceLocation ENTITY_ANIM_DATA_SYNC_PACKET_ID = AzureLib.modResource("entity_anim_data_sync"); + @Deprecated(forRemoval = true) ResourceLocation ENTITY_ANIM_TRIGGER_SYNC_PACKET_ID = AzureLib.modResource("entity_anim_trigger_sync"); + @Deprecated(forRemoval = true) ResourceLocation BLOCK_ENTITY_ANIM_DATA_SYNC_PACKET_ID = AzureLib.modResource("block_entity_anim_data_sync"); + @Deprecated(forRemoval = true) ResourceLocation BLOCK_ENTITY_ANIM_TRIGGER_SYNC_PACKET_ID = AzureLib.modResource("block_entity_anim_trigger_sync"); + @Deprecated(forRemoval = true) ResourceLocation CUSTOM_ENTITY_ID = AzureLib.modResource("spawn_entity"); + @Deprecated(forRemoval = true) Map SYNCED_ANIMATABLES = new Object2ObjectOpenHashMap<>(); + ResourceLocation AZ_BLOCKENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID = AzureLib.modResource( + "az_blockentity_dispatch_command_sync" + ); + + ResourceLocation AZ_ENTITY_ANIM_TRIGGER_SYNC_PACKET_ID = AzureLib.modResource("az_entity_anim_trigger_sync"); + + ResourceLocation AZ_ENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID = AzureLib.modResource( + "az_entity_dispatch_command_sync" + ); + + ResourceLocation AZ_ITEM_STACK_DISPATCH_COMMAND_SYNC_PACKET_ID = AzureLib.modResource( + "az_item_stack_dispatch_command_sync" + ); + /** * Registers a synced {@link GeoAnimatable} object for networking support.
* It is recommended that you don't call this directly, instead implementing and calling {@link mod.azure.azurelib.animatable.SingletonGeoAnimatable#registerSyncedAnimatable} */ + @Deprecated(forRemoval = true) default void registerSyncedAnimatable(GeoAnimatable animatable) { synchronized (this) { GeoAnimatable existing = SYNCED_ANIMATABLES.put(animatable.getClass().toString(), animatable); @@ -46,11 +69,13 @@ default void registerSyncedAnimatable(GeoAnimatable animatable) { } } + @Deprecated(forRemoval = true) Packet createPacket(Entity entity); /** * Used to register packets that the server sends **/ + @Deprecated(forRemoval = true) void registerClientReceiverPackets(); void sendToTrackingEntityAndSelf(AbstractPacket packet, Entity entityToTrack); @@ -71,6 +96,7 @@ interface IPacketCallback { * @param className the className */ @Nullable + @Deprecated(forRemoval = true) static GeoAnimatable getSyncedAnimatable(String className) { GeoAnimatable animatable = SYNCED_ANIMATABLES.get(className); diff --git a/common/src/main/java/mod/azure/azurelib/renderer/DyeableGeoArmorRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/DyeableGeoArmorRenderer.java index 7bf7e55c5..976a2ac43 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/DyeableGeoArmorRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/DyeableGeoArmorRenderer.java @@ -26,6 +26,7 @@ /** * A dyeable armour renderer for AzureLib armor models. */ +@Deprecated(forRemoval = true) public abstract class DyeableGeoArmorRenderer extends GeoArmorRenderer { protected final Set dyeableBones = new ObjectArraySet<>(); protected BakedGeoModel lastModel = null; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/DynamicGeoEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/DynamicGeoEntityRenderer.java index e401e5287..f4c26bc52 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/DynamicGeoEntityRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/DynamicGeoEntityRenderer.java @@ -38,6 +38,7 @@ * Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, * and consider whether the benefits are worth the cost for your needs. */ +@Deprecated(forRemoval = true) public abstract class DynamicGeoEntityRenderer extends GeoEntityRenderer { protected static final Map TEXTURE_DIMENSIONS_CACHE = new Object2ObjectOpenHashMap<>(); diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRenderer.java index 228a0d1ca..4a011471c 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRenderer.java @@ -48,6 +48,7 @@ * @see GeoItem * @param */ +@Deprecated(forRemoval = true) public class GeoArmorRenderer extends HumanoidModel implements GeoRenderer { protected final GeoRenderLayersContainer renderLayers = new GeoRenderLayersContainer<>(this); protected final GeoModel model; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRendererConstants.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRendererConstants.java index 1b11a15f0..dff2461fb 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRendererConstants.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRendererConstants.java @@ -6,6 +6,7 @@ /** * @author Boston Vanseghi */ +@Deprecated(forRemoval = true) public class GeoArmorRendererConstants { public static final String BONE_ARMOR_BODY_NAME = "armorBody"; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoBlockRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoBlockRenderer.java index 639812ae7..e9889effa 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/GeoBlockRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoBlockRenderer.java @@ -43,6 +43,7 @@ * Base {@link GeoRenderer} class for rendering {@link BlockEntity Blocks} specifically.
* All blocks added to be rendered by AzureLib should use an instance of this class. */ +@Deprecated(forRemoval = true) public class GeoBlockRenderer implements GeoRenderer, BlockEntityRenderer { protected final GeoModel model; protected final GeoRenderLayersContainer renderLayers = new GeoRenderLayersContainer<>(this); diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoEntityRenderer.java index c5364d92d..6dc0e42cc 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/GeoEntityRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoEntityRenderer.java @@ -50,6 +50,7 @@ * All entities added to be rendered by AzureLib should use an instance of this class.
* This also includes {@link net.minecraft.world.entity.projectile.Projectile Projectiles} */ +@Deprecated(forRemoval = true) public class GeoEntityRenderer extends EntityRenderer implements GeoRenderer { protected final List> renderLayers = new ObjectArrayList<>(); protected final GeoModel model; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoItemRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoItemRenderer.java index fbf6d944f..46b367217 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/GeoItemRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoItemRenderer.java @@ -46,6 +46,7 @@ * Base {@link GeoRenderer} class for rendering {@link Item Items} specifically.
* All items added to be rendered by AzureLib should use an instance of this class. */ +@Deprecated(forRemoval = true) public class GeoItemRenderer extends BlockEntityWithoutLevelRenderer implements GeoRenderer { protected final GeoRenderLayersContainer renderLayers = new GeoRenderLayersContainer<>(this); protected final GeoModel model; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoObjectRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoObjectRenderer.java index c3a63833e..7405c6558 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/GeoObjectRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoObjectRenderer.java @@ -38,6 +38,7 @@ *
* It is strongly recommended you override {@link GeoRenderer#getInstanceId} if using this renderer */ +@Deprecated(forRemoval = true) public class GeoObjectRenderer implements GeoRenderer { protected final GeoRenderLayersContainer renderLayers = new GeoRenderLayersContainer<>(this); protected final GeoModel model; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoRenderer.java index 93675ac24..e5396ea56 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/GeoRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoRenderer.java @@ -33,6 +33,7 @@ /** * Base interface for all AzureLib renderers.
*/ +@Deprecated(forRemoval = true) public interface GeoRenderer { /** * Gets the model instance for this renderer diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoReplacedEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoReplacedEntityRenderer.java index f1e964f0d..1b3022aa9 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/GeoReplacedEntityRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoReplacedEntityRenderer.java @@ -49,6 +49,7 @@ /** * An alternate to {@link GeoEntityRenderer}, used specifically for replacing existing non-AzureLib entities with AzureLib rendering dynamically, without the need for an additional entity class */ +@Deprecated(forRemoval = true) public class GeoReplacedEntityRenderer extends EntityRenderer implements GeoRenderer { protected final GeoModel model; protected final List> renderLayers = new ObjectArrayList<>(); diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoArmorRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoArmorRenderer.java index 033d8a48f..09f02c8fb 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoArmorRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoArmorRenderer.java @@ -33,6 +33,7 @@ * Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider * whether the benefits are worth the cost for your needs. */ +@Deprecated(forRemoval = true) public abstract class DynamicGeoArmorRenderer extends GeoArmorRenderer { protected static final Map TEXTURE_DIMENSIONS_CACHE = diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoBlockRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoBlockRenderer.java index 3caab70e3..ea7ba3eba 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoBlockRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoBlockRenderer.java @@ -32,6 +32,7 @@ * Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider * whether the benefits are worth the cost for your needs. */ +@Deprecated(forRemoval = true) public abstract class DynamicGeoBlockRenderer extends GeoBlockRenderer { protected static final Map TEXTURE_DIMENSIONS_CACHE = diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoItemRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoItemRenderer.java index c1536f279..71f9de7cf 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoItemRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoItemRenderer.java @@ -32,6 +32,7 @@ * Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider * whether the benefits are worth the cost for your needs. */ +@Deprecated(forRemoval = true) public abstract class DynamicGeoItemRenderer extends GeoItemRenderer { protected static final Map TEXTURE_DIMENSIONS_CACHE = diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoObjectRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoObjectRenderer.java index 530fd8eb6..1db0ad195 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoObjectRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoObjectRenderer.java @@ -31,6 +31,7 @@ * Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider * whether the benefits are worth the cost for your needs. */ +@Deprecated(forRemoval = true) public abstract class DynamicGeoObjectRenderer extends GeoObjectRenderer { protected static final Map TEXTURE_DIMENSIONS_CACHE = diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoReplacedEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoReplacedEntityRenderer.java index c46c2b2db..83de8e986 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoReplacedEntityRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoReplacedEntityRenderer.java @@ -33,6 +33,7 @@ * Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider * whether the benefits are worth the cost for your needs. */ +@Deprecated(forRemoval = true) public abstract class DynamicGeoReplacedEntityRenderer extends GeoReplacedEntityRenderer { protected static final Map TEXTURE_DIMENSIONS_CACHE = diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/AutoGlowingGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/AutoGlowingGeoLayer.java index a2d7ca3ab..6303a0e04 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/layer/AutoGlowingGeoLayer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/AutoGlowingGeoLayer.java @@ -22,6 +22,7 @@ /** * {@link GeoRenderLayer} for rendering the auto-generated glowlayer functionality implemented by AzureLib using the _glowing appendixed texture files. */ +@Deprecated(forRemoval = true) public class AutoGlowingGeoLayer extends GeoRenderLayer { public AutoGlowingGeoLayer(GeoRenderer renderer) { super(renderer); diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/BlockAndItemGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/BlockAndItemGeoLayer.java index 55ec94608..8b6bb0850 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/layer/BlockAndItemGeoLayer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/BlockAndItemGeoLayer.java @@ -30,6 +30,7 @@ /** * {@link GeoRenderLayer} for rendering {@link net.minecraft.world.level.block.state.BlockState BlockStates} or {@link net.minecraft.world.item.ItemStack ItemStacks} on a given {@link GeoAnimatable} */ +@Deprecated(forRemoval = true) public class BlockAndItemGeoLayer extends GeoRenderLayer { protected final BiFunction stackForBone; protected final BiFunction blockForBone; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/BoneFilterGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/BoneFilterGeoLayer.java index e55bf3b64..f5f3b2e38 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/layer/BoneFilterGeoLayer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/BoneFilterGeoLayer.java @@ -24,6 +24,7 @@ *
* NOTE: Despite this layer existing, it is much more efficient to use {@link FastBoneFilterGeoLayer} instead */ +@Deprecated(forRemoval = true) public class BoneFilterGeoLayer extends GeoRenderLayer { protected final TriConsumer checkAndApply; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/FastBoneFilterGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/FastBoneFilterGeoLayer.java index b4adead5b..b01e37b23 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/layer/FastBoneFilterGeoLayer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/FastBoneFilterGeoLayer.java @@ -26,6 +26,7 @@ * This version requires you provide the list of bones to filter up-front, * so that the bone hierarchy doesn't need to be traversed. */ +@Deprecated(forRemoval = true) public class FastBoneFilterGeoLayer extends BoneFilterGeoLayer { protected final Supplier> boneSupplier; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayer.java index 85f66b29a..042a2a816 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayer.java @@ -23,6 +23,7 @@ * Render layer base class for rendering additional layers of effects or textures over an existing model at runtime.
* Contains the base boilerplate and helper code for various render layer features */ +@Deprecated(forRemoval = true) public abstract class GeoRenderLayer { protected final GeoRenderer renderer; diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayersContainer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayersContainer.java index a4888c7ca..23c247507 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayersContainer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayersContainer.java @@ -17,6 +17,7 @@ * Base interface for a container for {@link GeoRenderLayer GeoRenderLayers}
* Each renderer should contain an instance of this, for holding its layers and handling events. */ +@Deprecated(forRemoval = true) public class GeoRenderLayersContainer { private final GeoRenderer renderer; private final List> layers = new ObjectArrayList<>(); diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/ItemArmorGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/ItemArmorGeoLayer.java index d5517d844..213c09fcf 100644 --- a/common/src/main/java/mod/azure/azurelib/renderer/layer/ItemArmorGeoLayer.java +++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/ItemArmorGeoLayer.java @@ -9,7 +9,6 @@ import java.util.Map; -import mod.azure.azurelib.platform.Services; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -60,6 +59,7 @@ * Supports both {@link mod.azure.azurelib.animatable.GeoItem AzureLib} and {@link net.minecraft.world.item.ArmorItem Vanilla} armor models.
* Unlike a traditional armor renderer, this renderer renders per-bone, giving much more flexible armor rendering. */ +@Deprecated(forRemoval = true) public class ItemArmorGeoLayer extends GeoRenderLayer { protected static final Map ARMOR_PATH_CACHE = new Object2ObjectOpenHashMap<>(); protected static final HumanoidModel INNER_ARMOR_MODEL = new HumanoidModel<>(Minecraft.getInstance().getEntityModels().bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/AzResourceCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/AzResourceCache.java new file mode 100644 index 000000000..731f066cc --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/AzResourceCache.java @@ -0,0 +1,59 @@ +package mod.azure.azurelib.rewrite; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; + +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * AzResourceCache is an abstract base class designed for managing and loading mod resources asynchronously. This class + * provides helper functions for loading and processing resource files of a specific type and storing them in a cache. + */ +public abstract class AzResourceCache { + + private static final Set EXCLUDED_NAMESPACES = ObjectOpenHashSet.of( + "moreplayermodels", + "customnpcs", + "creeperoverhaul", + "geckolib", + "gunsrpg", + "born_in_chaos_v1", + "neoforge" + ); + + protected final CompletableFuture loadResources( + Executor executor, + ResourceManager resourceManager, + String type, + Function loader, + BiConsumer map + ) { + return CompletableFuture.supplyAsync( + () -> resourceManager.listResources(type, fileName -> fileName.toString().endsWith(".json")), + executor + ) + .thenApplyAsync(resources -> { + var tasks = new Object2ObjectOpenHashMap>(); + + for (var resource : resources.keySet()) { + tasks.put(resource, CompletableFuture.supplyAsync(() -> loader.apply(resource), executor)); + } + + return tasks; + }, executor) + .thenAcceptAsync(tasks -> { + for (var entry : tasks.entrySet()) { + if (!EXCLUDED_NAMESPACES.contains(entry.getKey().getNamespace().toLowerCase(Locale.ROOT))) { + map.accept(entry.getKey(), entry.getValue().join()); + } + } + }, executor); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationContext.java new file mode 100644 index 000000000..1d22bacb5 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationContext.java @@ -0,0 +1,71 @@ +package mod.azure.azurelib.rewrite.animation; + +import mod.azure.azurelib.rewrite.animation.cache.AzBoneCache; + +/** + * The {@code AzAnimationContext} class provides a context for managing animation-related state and behaviors for + * animatable objects of type {@code T}. It serves as the central point of interaction between the animation system, + * configuration settings, and the animated object itself. + * + * @param The type of the animatable object that this context operates on. + */ +public class AzAnimationContext { + + private final AzBoneCache boneCache; + + private final AzAnimatorConfig config; + + private final AzAnimationTimer timer; + + // Package-private for mutability purposes. + T animatable; + + public AzAnimationContext( + AzBoneCache boneCache, + AzAnimatorConfig config, + AzAnimationTimer timer + ) { + this.boneCache = boneCache; + this.config = config; + this.timer = timer; + } + + /** + * Returns the current animatable instance associated with this animation context. + * + * @return The animatable instance of type {@code T}. + */ + public T animatable() { + return animatable; + } + + /** + * Returns the bone cache associated with the animation context. The bone cache is responsible for storing and + * managing bone-related data and transformations used during animations. + * + * @return The {@link AzBoneCache} instance managing bone data and transformations for animations. + */ + public AzBoneCache boneCache() { + return boneCache; + } + + /** + * Returns the animation configuration associated with this animation context. The configuration defines behavior + * such as bone reset speed, handling of missing bones, and whether animations should play while the game is paused. + * + * @return The {@link AzAnimatorConfig} instance containing the animation settings for this context. + */ + public AzAnimatorConfig config() { + return config; + } + + /** + * Returns the animation timer associated with the animation context. The timer is used to track animation progress + * over time and manage timing adjustments based on game state, such as pausing and resuming. + * + * @return The {@link AzAnimationTimer} instance responsible for animation timing. + */ + public AzAnimationTimer timer() { + return timer; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationTimer.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationTimer.java new file mode 100644 index 000000000..7a60686b7 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationTimer.java @@ -0,0 +1,91 @@ +package mod.azure.azurelib.rewrite.animation; + +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.client.Minecraft; + +/** + * AzAnimationTimer is responsible for managing animation progression based on game events and time deltas. It keeps + * track of the current animation time and ensures smooth transitions during various game states, such as pausing and + * resuming.
+ * The class relies on the provided {@link AzAnimatorConfig} for configurable behaviors, such as whether animations + * continue during game pauses or specific error handling preferences. + */ +public class AzAnimationTimer { + + private final AzAnimatorConfig config; + + // Remnants from GeoModel. + private double animTime; + + private double lastGameTickTime; + + private boolean wasPausedLastFrame; + + /** + * Constructs a new instance of AzAnimationTimer with the given configuration. + * + * @param config The configuration settings used to configure the animation timer. It includes parameters such as + * bone reset time, behavior during game pause, and whether to crash if a bone is missing. + */ + public AzAnimationTimer(AzAnimatorConfig config) { + this.config = config; + } + + /** + * Updates the animation timer by calculating the time delta since the last frame and applying it to the internal + * animation time. This method handles game pause states and adjusts the time calculations accordingly.
+ * Behavior: If the game is paused: + *
    + *
  • Sets an internal flag to indicate the paused state.
  • + *
  • Returns immediately if animations should not play while paused.
  • + *
+ * If transitioning from paused to unpaused: + *
    + *
  • Resets the frame delta to prevent large time skips in animations.
  • + *
+ * Accumulates the computed time delta into the animation time tracker to control the progression of animations. + */ + public void tick() { + var minecraft = Minecraft.getInstance(); + var currentRenderTick = RenderUtils.getCurrentTick(); + + if (minecraft.isPaused()) { + if (!wasPausedLastFrame) { + // If this is the first frame of the game pause time, we need to set a flag. + this.wasPausedLastFrame = true; + } + + if (!config.shouldPlayAnimationsWhileGamePaused()) { + // If we cannot play animations while the game is paused, then return early. + return; + } + } + + // Compute the delta render tick for this frame. This calculation by default does not account for the game + // pause state, which means that the difference here could be massive by the time the player unpauses. + var deltaRenderTick = currentRenderTick - lastGameTickTime; + + if (wasPausedLastFrame && !minecraft.isPaused()) { + // If this is the first frame of the game play time, we need to set a flag and adjust the deltaRenderTick. + this.wasPausedLastFrame = false; + // To account for the deltaRenderTick being massive on exiting the game pause state, we simply set + // it to 0. This will result in no difference being added to animTime, allowing animations to + // continue right where it left off. + deltaRenderTick = 0; + } + + // Add the deltaRenderTick to animTime. animTime is what controls the progress of animations. + this.animTime += deltaRenderTick; + this.lastGameTickTime = currentRenderTick; + } + + /** + * Retrieves the current animation time. + * + * @return The current animation time as a double value, representing the accumulated time used for the progression + * of animations. + */ + public double getAnimTime() { + return animTime; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java new file mode 100644 index 000000000..bc707291d --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java @@ -0,0 +1,134 @@ +package mod.azure.azurelib.rewrite.animation; + +import mod.azure.azurelib.AzureLibException; +import mod.azure.azurelib.core.molang.MolangParser; +import mod.azure.azurelib.core.molang.MolangQueries; +import mod.azure.azurelib.rewrite.animation.cache.AzBakedAnimationCache; +import mod.azure.azurelib.rewrite.animation.cache.AzBoneCache; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; +import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimation; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * The {@code AzAnimator} class is an abstract base class for managing animations for various types of objects such as + * entities, blocks, or items. It provides a reusable structure for animating objects, allowing the integration of a + * variety of animation controllers and custom animations. + * + * @param The type of object this animator will animate (e.g., an entity, block entity, or item stack). + */ +public abstract class AzAnimator { + + private final AzAnimationContext reusableContext; + + // Holds animation controllers. + private final AzAnimationControllerContainer animationControllerContainer; + + public boolean reloadAnimations; + + protected AzAnimator() { + this(AzAnimatorConfig.defaultConfig()); + } + + protected AzAnimator(AzAnimatorConfig config) { + this.animationControllerContainer = new AzAnimationControllerContainer<>(); + + var boneCache = new AzBoneCache(); + var timer = new AzAnimationTimer(config); + + this.reusableContext = new AzAnimationContext<>(boneCache, config, timer); + } + + public abstract void registerControllers(AzAnimationControllerContainer animationControllerContainer); + + public abstract @NotNull ResourceLocation getAnimationLocation(T animatable); + + public void animate(T animatable, float partialTicks) { + reusableContext.animatable = animatable; + + var boneCache = reusableContext.boneCache(); + var timer = reusableContext.timer(); + + timer.tick(); + + preAnimationSetup(animatable, timer.getAnimTime()); + + if (!boneCache.isEmpty()) { + + for (var controller : animationControllerContainer.getAll()) { + controller.update(); + } + + this.reloadAnimations = false; + + boneCache.update(reusableContext); + } + + setCustomAnimations(animatable, partialTicks); + } + + /** + * Apply transformations and settings prior to acting on any animation-related functionality + */ + protected void preAnimationSetup(T animatable, double animTime) { + applyMolangQueries(animatable, animTime); + } + + protected void applyMolangQueries(T animatable, double animTime) { + var level = Objects.requireNonNull(Minecraft.getInstance().level); + var parser = MolangParser.INSTANCE; + + parser.setMemoizedValue(MolangQueries.LIFE_TIME, () -> animTime / 20d); + parser.setMemoizedValue(MolangQueries.ACTOR_COUNT, level::getEntityCount); + parser.setMemoizedValue(MolangQueries.TIME_OF_DAY, () -> level.getDayTime() / 24000f); + parser.setMemoizedValue(MolangQueries.MOON_PHASE, level::getMoonPhase); + } + + /** + * Sets custom animations for the given animatable object. This method is used to define and configure specific + * animations unique to the context of the animatable and the current render state. + * + * @param animatable The object for which custom animations are being set. + * @param partialTicks The partial tick time used for interpolating animations smoothly between frames. + */ + public void setCustomAnimations(T animatable, float partialTicks) {} + + public void setActiveModel(AzBakedModel model) { + var modelChanged = reusableContext.boneCache().setActiveModel(model); + + if (modelChanged) { + // If the model changed, we need to clear the bone animation queue cache for every controller. + // TODO: We shouldn't have to remember to do this. If the baked model changes, then the bone cache + // should be re-instantiated. If the bone cache is re-instantiated, then so should the bone animation + // queue caches. + animationControllerContainer.getAll() + .forEach(controller -> controller.boneAnimationQueueCache().clear()); + } + } + + /** + * Get the baked animation object used for rendering from the given resource path + */ + public AzBakedAnimation getAnimation(T animatable, String name) { + var location = getAnimationLocation(animatable); + var bakedAnimations = AzBakedAnimationCache.getInstance().getNullable(location); + + if (bakedAnimations == null) { + throw new AzureLibException(location, "Unable to find animation."); + } + + return bakedAnimations.getAnimation(name); + } + + public AzAnimationContext context() { + return reusableContext; + } + + public AzAnimationControllerContainer getAnimationControllerContainer() { + return animationControllerContainer; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorAccessor.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorAccessor.java new file mode 100644 index 000000000..afa249885 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorAccessor.java @@ -0,0 +1,37 @@ +package mod.azure.azurelib.rewrite.animation; + +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +/** + * The {@code AzAnimatorAccessor} interface provides a mechanism to associate and manage an {@link AzAnimator} instance + * with a target object. This enables retrieval and manipulation of animator instances that are specific to the target + * object. + * + * @param The type of the target object that the animator applies to. + */ +public interface AzAnimatorAccessor { + + @Nullable + AzAnimator getAnimatorOrNull(); + + void setAnimator(AzAnimator animator); + + default Optional> getAnimator() { + return Optional.ofNullable(getAnimatorOrNull()); + } + + @SuppressWarnings("unchecked") + static AzAnimatorAccessor cast(T target) { + return (AzAnimatorAccessor) target; + } + + static AzAnimator getOrNull(T target) { + return cast(target).getAnimatorOrNull(); + } + + static Optional> get(T target) { + return Optional.ofNullable(getOrNull(target)); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorConfig.java new file mode 100644 index 000000000..1ab8fa9da --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorConfig.java @@ -0,0 +1,108 @@ +package mod.azure.azurelib.rewrite.animation; + +/** + * The {@code AzAnimatorConfig} record encapsulates configuration settings related to the animation system of the + * AzureLib framework. It provides customizable options for controlling animation behavior and error handling. This + * configuration is used to determine runtime behaviors such as whether animations should continue while the game is + * paused, whether the system should throw an error if a bone in the animation structure is missing, and the duration it + * takes to reset bone transformations. + * + * @param boneResetTime The specified time duration (in ticks or seconds) for resetting bones to + * their default transformations when animations are interrupted. + * @param crashIfBoneMissing Specifies whether the system will throw an exception if an expected bone + * in the animation is not found during runtime. + * @param shouldPlayAnimationsWhileGamePaused Indicates whether animations should continue playing when the game is + * paused. + */ +public record AzAnimatorConfig( + double boneResetTime, + boolean crashIfBoneMissing, + boolean shouldPlayAnimationsWhileGamePaused +) { + + /** + * Creates a new instance of the {@link Builder} to configure and build an {@code AzAnimatorConfig}. + * + * @return A new {@code Builder} instance for constructing an {@code AzAnimatorConfig}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a default {@link AzAnimatorConfig} instance with predefined settings. The default configuration typically + * includes settings such as: + *
    + *
  • A bone reset time of 1 tick/second.
  • + *
  • Disabling the feature to crash if a bone is missing.
  • + *
  • Disabling animations while the game is paused.
  • + *
+ * + * @return The default configuration instance of {@code AzAnimatorConfig}, built with default values. + */ + public static AzAnimatorConfig defaultConfig() { + return builder().build(); + } + + public static class Builder { + + private double boneResetTime; + + private boolean crashIfBoneMissing; + + private boolean shouldPlayAnimationsWhileGamePaused; + + private Builder() { + this.boneResetTime = 1; + this.crashIfBoneMissing = false; + this.shouldPlayAnimationsWhileGamePaused = false; + } + + /** + * Configures the builder to crash if a required bone is missing during animation setup. + * + * @return The current Builder instance for method chaining. + */ + public Builder crashIfBoneMissing() { + this.crashIfBoneMissing = true; + return this; + } + + /** + * Configures the builder to enable animations to play even when the game is paused. + * + * @return The current Builder instance for method chaining. + */ + public Builder shouldPlayAnimationsWhileGamePaused() { + this.shouldPlayAnimationsWhileGamePaused = true; + return this; + } + + /** + * Sets the bone reset time duration. This value determines how long it takes to reset bones to their default + * state after an animation is completed. + * + * @param boneResetTime The duration (in seconds) for bone reset time. + * @return The current Builder instance to allow method chaining. + */ + public Builder withBoneResetTime(double boneResetTime) { + this.boneResetTime = boneResetTime; + return this; + } + + /** + * Constructs a new {@link AzAnimatorConfig} instance with the specified configuration parameters defined in the + * Builder. The configuration includes options for bone reset timing, error handling when bones are missing, and + * animation playback behavior during game pause state. + * + * @return A new {@code AzAnimatorConfig} instance containing the configured settings. + */ + public AzAnimatorConfig build() { + return new AzAnimatorConfig( + boneResetTime, + crashIfBoneMissing, + shouldPlayAnimationsWhileGamePaused + ); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzBoneAnimationUpdateUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzBoneAnimationUpdateUtil.java new file mode 100644 index 000000000..16b10fad2 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzBoneAnimationUpdateUtil.java @@ -0,0 +1,107 @@ +package mod.azure.azurelib.rewrite.animation; + +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzBoneAnimationQueue; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingUtil; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.model.AzBoneSnapshot; + +public class AzBoneAnimationUpdateUtil { + + /** + * Updates the position of the given bone by interpolating the position values from the animation queue and applying + * the specified easing type. The method also updates the snapshot offsets and flags the bone's position as changed. + * + * @param boneAnimation The animation queue containing position data for the bone. + * @param bone The bone whose position is being updated. + * @param easingType The easing type used for interpolating the position values. + * @param snapshot The snapshot used to store the updated position offsets and start animations. + */ + public static void updatePositions( + AzBoneAnimationQueue boneAnimation, + AzBone bone, + AzEasingType easingType, + AzBoneSnapshot snapshot + ) { + var posXPoint = boneAnimation.positionXQueue().poll(); + var posYPoint = boneAnimation.positionYQueue().poll(); + var posZPoint = boneAnimation.positionZQueue().poll(); + + if (posXPoint != null && posYPoint != null && posZPoint != null) { + bone.setPosX((float) AzEasingUtil.lerpWithOverride(posXPoint, easingType)); + bone.setPosY((float) AzEasingUtil.lerpWithOverride(posYPoint, easingType)); + bone.setPosZ((float) AzEasingUtil.lerpWithOverride(posZPoint, easingType)); + snapshot.updateOffset(bone.getPosX(), bone.getPosY(), bone.getPosZ()); + snapshot.startPosAnim(); + bone.markPositionAsChanged(); + } + } + + /** + * Updates the rotation of the specified bone by interpolating the rotation values from the animation queue and + * applying the specified easing type. The method also updates the snapshot rotation values, starts the rotation + * animation, and marks the bone's rotation as changed. + * + * @param boneAnimation The animation queue containing rotation data for the bone. + * @param bone The bone whose rotation is being updated. + * @param easingType The easing type used for interpolating the rotation values. + * @param initialSnapshot The initial snapshot containing the original rotation offsets. + * @param snapshot The snapshot used to store the updated rotation values and start animations. + */ + public static void updateRotations( + AzBoneAnimationQueue boneAnimation, + AzBone bone, + AzEasingType easingType, + AzBoneSnapshot initialSnapshot, + AzBoneSnapshot snapshot + ) { + var rotXPoint = boneAnimation.rotationXQueue().poll(); + var rotYPoint = boneAnimation.rotationYQueue().poll(); + var rotZPoint = boneAnimation.rotationZQueue().poll(); + + if (rotXPoint != null && rotYPoint != null && rotZPoint != null) { + bone.setRotX( + (float) AzEasingUtil.lerpWithOverride(rotXPoint, easingType) + initialSnapshot.getRotX() + ); + bone.setRotY( + (float) AzEasingUtil.lerpWithOverride(rotYPoint, easingType) + initialSnapshot.getRotY() + ); + bone.setRotZ( + (float) AzEasingUtil.lerpWithOverride(rotZPoint, easingType) + initialSnapshot.getRotZ() + ); + snapshot.updateRotation(bone.getRotX(), bone.getRotY(), bone.getRotZ()); + snapshot.startRotAnim(); + bone.markRotationAsChanged(); + } + } + + /** + * Updates the scale of the specified bone by interpolating the scale values from the animation queue and applying + * the specified easing type. The method also updates the snapshot with the new scale values, starts the scale + * animation, and marks the bone's scale as changed. + * + * @param boneAnimation The animation queue containing scale data for the bone. + * @param bone The bone whose scale is being updated. + * @param easingType The easing type used for interpolating the scale values. + * @param snapshot The snapshot used to store the updated scale values and start animations. + */ + public static void updateScale( + AzBoneAnimationQueue boneAnimation, + AzBone bone, + AzEasingType easingType, + AzBoneSnapshot snapshot + ) { + var scaleXPoint = boneAnimation.scaleXQueue().poll(); + var scaleYPoint = boneAnimation.scaleYQueue().poll(); + var scaleZPoint = boneAnimation.scaleZQueue().poll(); + + if (scaleXPoint != null && scaleYPoint != null && scaleZPoint != null) { + bone.setScaleX((float) AzEasingUtil.lerpWithOverride(scaleXPoint, easingType)); + bone.setScaleY((float) AzEasingUtil.lerpWithOverride(scaleYPoint, easingType)); + bone.setScaleZ((float) AzEasingUtil.lerpWithOverride(scaleZPoint, easingType)); + snapshot.updateScale(bone.getScaleX(), bone.getScaleY(), bone.getScaleZ()); + snapshot.startScaleAnim(); + bone.markScaleAsChanged(); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzCachedBoneUpdateUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzCachedBoneUpdateUtil.java new file mode 100644 index 000000000..ea1906e4d --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzCachedBoneUpdateUtil.java @@ -0,0 +1,160 @@ +package mod.azure.azurelib.rewrite.animation; + +import mod.azure.azurelib.core.utils.Interpolations; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.model.AzBoneSnapshot; + +import java.util.Map; + +public class AzCachedBoneUpdateUtil { + + /** + * Updates the cached position of a given bone by interpolating its offsets towards its initial snapshot. Stops + * ongoing position animations if necessary and updates the bone's position based on the reset percentage. + * + * @param bone the bone whose position is to be updated + * @param boneSnapshots a map containing snapshots of bones by their names + * @param animTime the current animation time + * @param resetTickLength the duration over which the position reset occurs + */ + public static void updateCachedBonePosition( + AzBone bone, + Map boneSnapshots, + double animTime, + double resetTickLength + ) { + if (bone.hasPositionChanged()) { + return; + } + + var initialSnapshot = bone.getInitialAzSnapshot(); + var saveSnapshot = boneSnapshots.get(bone.getName()); + + if (saveSnapshot.isPosAnimInProgress()) { + saveSnapshot.stopPosAnim(animTime); + } + + var percentageReset = Math.min( + (animTime - saveSnapshot.getLastResetPositionTick()) / resetTickLength, + 1 + ); + + bone.setPosX( + (float) Interpolations.lerp( + saveSnapshot.getOffsetX(), + initialSnapshot.getOffsetX(), + percentageReset + ) + ); + bone.setPosY( + (float) Interpolations.lerp( + saveSnapshot.getOffsetY(), + initialSnapshot.getOffsetY(), + percentageReset + ) + ); + bone.setPosZ( + (float) Interpolations.lerp( + saveSnapshot.getOffsetZ(), + initialSnapshot.getOffsetZ(), + percentageReset + ) + ); + + if (percentageReset >= 1) { + saveSnapshot.updateOffset(bone.getPosX(), bone.getPosY(), bone.getPosZ()); + } + } + + /** + * Updates the cached rotation of a given bone by interpolating its rotation values towards its initial snapshot. + * Stops any ongoing rotation animations if necessary and updates the bone's rotation based on the reset percentage. + * + * @param bone the bone whose rotation is to be updated + * @param boneSnapshots a map containing snapshots of bones by their names + * @param animTime the current animation time + * @param resetTickLength the duration over which the rotation reset occurs + */ + public static void updateCachedBoneRotation( + AzBone bone, + Map boneSnapshots, + double animTime, + double resetTickLength + ) { + if (bone.hasRotationChanged()) { + return; + } + + var initialSnapshot = bone.getInitialAzSnapshot(); + var saveSnapshot = boneSnapshots.get(bone.getName()); + + if (saveSnapshot.isRotAnimInProgress()) { + saveSnapshot.stopRotAnim(animTime); + } + + double percentageReset = Math.min( + (animTime - saveSnapshot.getLastResetRotationTick()) / resetTickLength, + 1 + ); + + bone.setRotX( + (float) Interpolations.lerp(saveSnapshot.getRotX(), initialSnapshot.getRotX(), percentageReset) + ); + bone.setRotY( + (float) Interpolations.lerp(saveSnapshot.getRotY(), initialSnapshot.getRotY(), percentageReset) + ); + bone.setRotZ( + (float) Interpolations.lerp(saveSnapshot.getRotZ(), initialSnapshot.getRotZ(), percentageReset) + ); + + if (percentageReset >= 1) { + saveSnapshot.updateRotation(bone.getRotX(), bone.getRotY(), bone.getRotZ()); + } + } + + /** + * Updates the cached scale of a given bone by interpolating its scale values towards its initial snapshot. Stops + * any ongoing scale animations if necessary and updates the bone's scale based on the reset percentage. + * + * @param bone the bone whose scale is to be updated + * @param boneSnapshots a map containing snapshots of bones by their names + * @param animTime the current animation time + * @param resetTickLength the duration over which the scale reset occurs + */ + public static void updateCachedBoneScale( + AzBone bone, + Map boneSnapshots, + double animTime, + double resetTickLength + ) { + if (bone.hasScaleChanged()) { + return; + } + + var initialSnapshot = bone.getInitialAzSnapshot(); + var saveSnapshot = boneSnapshots.get(bone.getName()); + + if (saveSnapshot.isScaleAnimInProgress()) { + saveSnapshot.stopScaleAnim(animTime); + } + + double percentageReset = Math.min( + (animTime - saveSnapshot.getLastResetScaleTick()) / resetTickLength, + 1 + ); + + bone.setScaleX( + (float) Interpolations.lerp(saveSnapshot.getScaleX(), initialSnapshot.getScaleX(), percentageReset) + ); + bone.setScaleY( + (float) Interpolations.lerp(saveSnapshot.getScaleY(), initialSnapshot.getScaleY(), percentageReset) + ); + bone.setScaleZ( + (float) Interpolations.lerp(saveSnapshot.getScaleZ(), initialSnapshot.getScaleZ(), percentageReset) + ); + + if (percentageReset >= 1) { + saveSnapshot.updateScale(bone.getScaleX(), bone.getScaleY(), bone.getScaleZ()); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBakedAnimationCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBakedAnimationCache.java new file mode 100644 index 000000000..33702aee5 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBakedAnimationCache.java @@ -0,0 +1,54 @@ +package mod.azure.azurelib.rewrite.animation.cache; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mod.azure.azurelib.loading.FileLoader; +import mod.azure.azurelib.rewrite.AzResourceCache; +import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimations; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * AzBakedAnimationCache is a singleton cache to manage and store preloaded animation data of type + * {@link AzBakedAnimations}. It is an extension of {@link AzResourceCache} and provides mechanisms for managing + * animation resources in Minecraft modding. Aimed at efficient storage and retrieval, as well as background processing + * of animation data.
+ * Features: + *
    + *
  • Supports asynchronous loading of animation resources from the in-memory {@code ResourceManager}. + *
  • Caches animation data keyed by {@link ResourceLocation}. + *
  • Provides access to the cached animations or null values for non-existent records.
  • + *
+ */ +public class AzBakedAnimationCache extends AzResourceCache { + + private static final AzBakedAnimationCache INSTANCE = new AzBakedAnimationCache(); + + public static AzBakedAnimationCache getInstance() { + return INSTANCE; + } + + private final Map bakedAnimations; + + private AzBakedAnimationCache() { + this.bakedAnimations = new Object2ObjectOpenHashMap<>(); + } + + public CompletableFuture loadAnimations(Executor backgroundExecutor, ResourceManager resourceManager) { + return loadResources( + backgroundExecutor, + resourceManager, + "animations", + resource -> FileLoader.loadAzAnimationsFile(resource, resourceManager), + bakedAnimations::put + ); + } + + public @Nullable AzBakedAnimations getNullable(ResourceLocation resourceLocation) { + return bakedAnimations.get(resourceLocation); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBoneCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBoneCache.java new file mode 100644 index 000000000..57fd970ec --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBoneCache.java @@ -0,0 +1,87 @@ +package mod.azure.azurelib.rewrite.animation.cache; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mod.azure.azurelib.rewrite.animation.AzAnimationContext; +import mod.azure.azurelib.rewrite.animation.AzCachedBoneUpdateUtil; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.model.AzBoneSnapshot; + +import java.util.Map; +import java.util.Objects; + +/** + * The AzBoneCache class is responsible for managing the state and cache of bones in a baked model. It provides + * functionality for updating animation contexts, managing snapshots of bone states, and resetting transformation + * markers in preparation for rendering. + */ +public class AzBoneCache { + + private AzBakedModel bakedModel; + + private final Map boneSnapshotsByName; + + public AzBoneCache() { + this.bakedModel = AzBakedModel.EMPTY; + this.boneSnapshotsByName = new Object2ObjectOpenHashMap<>(); + } + + public boolean setActiveModel(AzBakedModel model) { + var willModelChange = !Objects.equals(bakedModel, model); + this.bakedModel = model; + + if (willModelChange) { + snapshot(); + } + + return willModelChange; + } + + public void update(AzAnimationContext context) { + var config = context.config(); + var timer = context.timer(); + var animTime = timer.getAnimTime(); + var boneSnapshots = getBoneSnapshotsByName(); + var resetTickLength = config.boneResetTime(); + + // Updates the cached bone snapshots (only if they have changed). + for (var bone : bakedModel.getBonesByName().values()) { + AzCachedBoneUpdateUtil.updateCachedBoneRotation(bone, boneSnapshots, animTime, resetTickLength); + AzCachedBoneUpdateUtil.updateCachedBonePosition(bone, boneSnapshots, animTime, resetTickLength); + AzCachedBoneUpdateUtil.updateCachedBoneScale(bone, boneSnapshots, animTime, resetTickLength); + } + + resetBoneTransformationMarkers(); + } + + /** + * Reset the transformation markers applied to each {@link AzBone} ready for the next render frame + */ + private void resetBoneTransformationMarkers() { + bakedModel.getBonesByName().values().forEach(AzBone::resetStateChanges); + } + + /** + * Create new bone {@link AzBoneSnapshot} based on the bone's initial snapshot for the currently registered + * {@link AzBone AzBones}, filtered by the bones already present in the master snapshots map + */ + private void snapshot() { + boneSnapshotsByName.clear(); + + for (var bone : bakedModel.getBonesByName().values()) { + boneSnapshotsByName.put(bone.getName(), AzBoneSnapshot.copy(bone.getInitialAzSnapshot())); + } + } + + public AzBakedModel getBakedModel() { + return bakedModel; + } + + public Map getBoneSnapshotsByName() { + return boneSnapshotsByName; + } + + public boolean isEmpty() { + return bakedModel.getBonesByName().isEmpty(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java new file mode 100644 index 000000000..d07913e51 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java @@ -0,0 +1,40 @@ +package mod.azure.azurelib.rewrite.animation.cache; + +import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * The AzIdentifiableItemStackAnimatorCache class is a singleton utility for managing a cache of {@link ItemStack} + * objects, each associated with a unique identifier (UUID). This class provides functionality to register and retrieve + * item animators that apply to specific {@link ItemStack}s using their respective UUIDs. + */ +public class AzIdentifiableItemStackAnimatorCache { + + private static final AzIdentifiableItemStackAnimatorCache INSTANCE = new AzIdentifiableItemStackAnimatorCache(); + + // TODO: Purge animators periodically. + private static final Map ANIMATORS_BY_UUID = new HashMap<>(); + + public static AzIdentifiableItemStackAnimatorCache getInstance() { + return INSTANCE; + } + + private AzIdentifiableItemStackAnimatorCache() {} + + public void add(ItemStack itemStack, AzItemAnimator animator) { + var uuid = itemStack.getTag().getUUID("az_id"); + + if (uuid != null) { + ANIMATORS_BY_UUID.computeIfAbsent(uuid, ($) -> animator); + } + } + + public @Nullable AzItemAnimator getOrNull(UUID uuid) { + return uuid == null ? null : ANIMATORS_BY_UUID.get(uuid); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentityRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentityRegistry.java new file mode 100644 index 000000000..b89b3c15f --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentityRegistry.java @@ -0,0 +1,39 @@ +package mod.azure.azurelib.rewrite.animation.cache; + +import net.minecraft.world.item.Item; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * The AzIdentityRegistry class provides functionality to register and check the identity of items. This class maintains + * a static registry of unique items, allowing you to determine if a specific item has been registered. + */ +public class AzIdentityRegistry { + + private static final Set IDENTITY_OF_ITEMS = new HashSet<>(); + + /** + * Registers one or more items into a static identity set, ensuring that the items are stored for identity tracking. + * + * @param first The first non-null item to be registered. This parameter is mandatory. + * @param rest A varargs array of additional items to register. These items can be null, but null values will not + * be added to the identity set. + */ + public static void register(@NotNull Item first, Item... rest) { + IDENTITY_OF_ITEMS.add(first); + IDENTITY_OF_ITEMS.addAll(Arrays.asList(rest)); + } + + /** + * Checks if the specified item exists in the identity set. + * + * @param item The item to check for identity. Must not be null. + * @return true if the item exists in the identity set, false otherwise. + */ + public static boolean hasIdentity(Item item) { + return IDENTITY_OF_ITEMS.contains(item); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAbstractAnimationController.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAbstractAnimationController.java new file mode 100644 index 000000000..a3a3f85a4 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAbstractAnimationController.java @@ -0,0 +1,33 @@ +package mod.azure.azurelib.rewrite.animation.controller; + +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequence; + +// TODO: This will eventually be usable in common-side code once animations are moved from assets to data. +public class AzAbstractAnimationController { + + private final String name; + + protected AzAnimationSequence currentSequence; + + protected AzDispatchSide currentSequenceOrigin; + + protected AzAbstractAnimationController(String name) { + this.name = name; + } + + public String name() { + return name; + } + + /** + * Checks whether the last animation that was playing on this controller has finished or not.
+ * This will return true if the controller has had an animation set previously, and it has finished playing and + * isn't going to loop or proceed to another animation.
+ * + * @return Whether the previous animation finished or not + */ + public boolean hasAnimationFinished() { + return currentSequence != null; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationController.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationController.java new file mode 100644 index 000000000..ea52d8459 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationController.java @@ -0,0 +1,210 @@ +package mod.azure.azurelib.rewrite.animation.controller; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeCallbacks; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeManager; +import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationPauseState; +import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationPlayState; +import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationStopState; +import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationTransitionState; +import mod.azure.azurelib.rewrite.animation.controller.state.machine.AzAnimationControllerStateMachine; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequence; +import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation; +import mod.azure.azurelib.rewrite.animation.property.AzAnimationProperties; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * The actual controller that handles the playing and usage of animations, including their various keyframes and + * instruction markers. Each controller can only play a single animation at a time - for example you may have one + * controller to animate walking, one to control attacks, one to control size, etc. + */ +public class AzAnimationController extends AzAbstractAnimationController { + + protected static final Logger LOGGER = LoggerFactory.getLogger(AzAnimationController.class); + + public static AzAnimationControllerBuilder builder(AzAnimator animator, String name) { + return new AzAnimationControllerBuilder<>(animator, name); + } + + private final AzAnimationControllerTimer controllerTimer; + + private final AzAnimationQueue animationQueue; + + private final AzAnimationControllerStateMachine stateMachine; + + private final AzAnimator animator; + + private final AzBoneAnimationQueueCache boneAnimationQueueCache; + + private final AzBoneSnapshotCache boneSnapshotCache; + + private final AzKeyframeManager keyframeManager; + + protected AzQueuedAnimation currentAnimation; + + private AzAnimationProperties animationProperties; + + AzAnimationController( + String name, + AzAnimator animator, + AzAnimationProperties animationProperties, + AzKeyframeCallbacks keyframeCallbacks + ) { + super(name); + + this.animator = animator; + this.controllerTimer = new AzAnimationControllerTimer<>(this); + this.animationProperties = animationProperties; + + this.animationQueue = new AzAnimationQueue(); + this.boneAnimationQueueCache = new AzBoneAnimationQueueCache<>(animator.context().boneCache()); + this.boneSnapshotCache = new AzBoneSnapshotCache(); + this.keyframeManager = new AzKeyframeManager<>( + this, + boneAnimationQueueCache, + boneSnapshotCache, + keyframeCallbacks + ); + + var stateHolder = new AzAnimationControllerStateMachine.StateHolder( + new AzAnimationPlayState<>(), + new AzAnimationPauseState<>(), + new AzAnimationStopState<>(), + new AzAnimationTransitionState<>() + ); + + this.stateMachine = new AzAnimationControllerStateMachine<>(stateHolder, this, animator.context()); + } + + @Override + public boolean hasAnimationFinished() { + return super.hasAnimationFinished() && stateMachine.isStopped(); + } + + public List tryCreateAnimationQueue(T animatable, AzAnimationSequence sequence) { + var stages = sequence.stages(); + var animations = new ArrayList(); + + for (var stage : stages) { + var animation = animator.getAnimation(animatable, stage.name()); + + if (animation == null) { + LOGGER.warn( + "Unable to find animation: {} for {}", + stage.name(), + animatable.getClass().getSimpleName() + ); + return List.of(); + } else { + animations.add(new AzQueuedAnimation(animation, stage.properties().playBehavior())); + } + } + + return animations; + } + + /** + * This method is called every frame in order to populate the animation point queues, and process animation state + * logic. + */ + public void update() { + // Adjust the tick before making any updates. + controllerTimer.update(); + // Run state machine updates. + stateMachine.update(); + // Update bone animation queue cache. + boneAnimationQueueCache.update(animationProperties.easingType()); + } + + public void run(AzDispatchSide originSide, @NotNull AzAnimationSequence sequence) { + if (currentSequenceOrigin == AzDispatchSide.SERVER && originSide == AzDispatchSide.CLIENT) { + if (!hasAnimationFinished()) { + // If we're playing a server-side sequence, ignore client-side sequences. + return; + } + } + + this.currentSequenceOrigin = originSide; + + if (stateMachine.isStopped()) { + stateMachine.transition(); + } + + if (currentSequence == null || !currentSequence.equals(sequence)) { + this.currentAnimation = null; + } + + var animatable = animator.context().animatable(); + + if (sequence.stages().isEmpty()) { + stateMachine.stop(); + return; + } + + if (!sequence.equals(currentSequence)) { + var animations = tryCreateAnimationQueue(animatable, sequence); + + if (!animations.isEmpty()) { + animationQueue.clear(); + animationQueue.addAll(animations); + this.currentSequence = sequence; + stateMachine.transition(); + return; + } + + stateMachine.stop(); + } + } + + public AzAnimationProperties animationProperties() { + return animationProperties; + } + + public void setAnimationProperties(AzAnimationProperties animationProperties) { + this.animationProperties = animationProperties; + } + + public AzAnimationQueue animationQueue() { + return animationQueue; + } + + public AzBoneAnimationQueueCache boneAnimationQueueCache() { + return boneAnimationQueueCache; + } + + public AzBoneSnapshotCache boneSnapshotCache() { + return boneSnapshotCache; + } + + public AzAnimationControllerTimer controllerTimer() { + return controllerTimer; + } + + public @Nullable AzQueuedAnimation currentAnimation() { + return currentAnimation; + } + + public AzKeyframeManager keyframeManager() { + return keyframeManager; + } + + public AzAnimationControllerStateMachine stateMachine() { + return stateMachine; + } + + public void setCurrentAnimation(AzQueuedAnimation currentAnimation) { + this.currentAnimation = currentAnimation; + + if (currentAnimation == null) { + this.currentSequence = null; + this.currentSequenceOrigin = null; + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerBuilder.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerBuilder.java new file mode 100644 index 000000000..0adbee4da --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerBuilder.java @@ -0,0 +1,64 @@ +package mod.azure.azurelib.rewrite.animation.controller; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeCallbacks; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; +import mod.azure.azurelib.rewrite.animation.property.AzAnimationProperties; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A builder class to construct {@link AzAnimationController} instances for managing animations in {@link AzAnimator}. + * This provides a fluent API to configure properties such as animation speed, keyframe callbacks, easing type, + * transition length, and triggerable animations. + * + * @param The type of object that the animation controller will operate on. + */ +public class AzAnimationControllerBuilder { + + private final AzAnimator animator; + + private final String name; + + private AzAnimationProperties animationProperties; + + private AzKeyframeCallbacks keyframeCallbacks; + + public AzAnimationControllerBuilder(AzAnimator animator, String name) { + this.animator = animator; + this.name = name; + this.animationProperties = AzAnimationProperties.DEFAULT; + this.keyframeCallbacks = AzKeyframeCallbacks.noop(); + } + + public AzAnimationControllerBuilder setAnimationSpeed(double animationSpeed) { + animationProperties = animationProperties.withAnimationSpeed(animationSpeed); + return this; + } + + public AzAnimationControllerBuilder setKeyframeCallbacks(@NotNull AzKeyframeCallbacks keyframeCallbacks) { + Objects.requireNonNull(keyframeCallbacks); + this.keyframeCallbacks = keyframeCallbacks; + return this; + } + + public AzAnimationControllerBuilder setEasingType(AzEasingType easingType) { + animationProperties = animationProperties.withEasingType(easingType); + return this; + } + + public AzAnimationControllerBuilder setTransitionLength(int transitionLength) { + animationProperties = animationProperties.withTransitionLength(transitionLength); + return this; + } + + public AzAnimationController build() { + return new AzAnimationController<>( + name, + animator, + animationProperties, + keyframeCallbacks + ); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerContainer.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerContainer.java new file mode 100644 index 000000000..d62bad1b3 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerContainer.java @@ -0,0 +1,39 @@ +package mod.azure.azurelib.rewrite.animation.controller; + +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; + +/** + * A container class for managing a collection of {@link AzAnimationController} instances. Provides methods to add, + * retrieve, and access animation controllers by their names. + * + * @param the type of the animation data or state managed by {@link AzAnimationController}. + */ +public class AzAnimationControllerContainer { + + private final Map> animationControllersByName; + + public AzAnimationControllerContainer() { + this.animationControllersByName = new Object2ObjectArrayMap<>(); + } + + @SafeVarargs + public final void add(AzAnimationController controller, AzAnimationController... controllers) { + animationControllersByName.put(controller.name(), controller); + + for (var extraController : controllers) { + animationControllersByName.put(extraController.name(), extraController); + } + } + + public @Nullable AzAnimationController getOrNull(String controllerName) { + return animationControllersByName.get(controllerName); + } + + public Collection> getAll() { + return animationControllersByName.values(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerTimer.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerTimer.java new file mode 100644 index 000000000..35bc94785 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerTimer.java @@ -0,0 +1,48 @@ +package mod.azure.azurelib.rewrite.animation.controller; + +/** + * A timer utility that integrates directly with an {@link AzAnimationController} to track and adjust tick values for + * animation playback control, based on the controller's state and animation speed modifiers. + * + * @param The type of the animatable entity being controlled by the animation controller. + */ +public class AzAnimationControllerTimer { + + private final AzAnimationController animationController; + + private double adjustedTick; + + private double tickOffset; + + public AzAnimationControllerTimer(AzAnimationController animationController) { + this.animationController = animationController; + } + + /** + * Adjust a tick value depending on the controller's current state and speed modifier.
+ * Is used when starting a new animation, transitioning, and a few other key areas + */ + public void update() { + var stateMachine = animationController.stateMachine(); + var animContext = stateMachine.getContext().animationContext(); + var animationSpeed = animationController.animationProperties().animationSpeed(); + var tick = animContext.timer().getAnimTime(); + + adjustedTick = animationSpeed * Math.max(tick - tickOffset, 0); + } + + public void reset() { + var stateMachine = animationController.stateMachine(); + var animContext = stateMachine.getContext().animationContext(); + this.tickOffset = animContext.timer().getAnimTime(); + this.adjustedTick = 0; + } + + public double getAdjustedTick() { + return adjustedTick; + } + + public void addToAdjustedTick(double adjustedTick) { + this.adjustedTick += adjustedTick; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationQueue.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationQueue.java new file mode 100644 index 000000000..e78b89f2d --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationQueue.java @@ -0,0 +1,51 @@ +package mod.azure.azurelib.rewrite.animation.controller; + +import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.Queue; + +/** + * Represents a queue of animations to be processed in a sequential manner. This class manages a collection of + * {@link AzQueuedAnimation} objects, allowing animations to be queued, retrieved, and cleared efficiently. It ensures + * that animations are processed in the order they are added.
+ *
+ * The queue supports operations to inspect the next animation without removal, retrieve and remove the next animation, + * add individual or multiple animations, and clear the entire queue. Additionally, it provides a method to determine if + * the queue is empty. + */ +public class AzAnimationQueue { + + private final Queue animationQueue; + + public AzAnimationQueue() { + this.animationQueue = new LinkedList<>(); + } + + public void add(@NotNull AzQueuedAnimation queuedAnimation) { + animationQueue.add(queuedAnimation); + } + + public void addAll(@NotNull Collection queuedAnimations) { + animationQueue.addAll(queuedAnimations); + } + + public @Nullable AzQueuedAnimation peek() { + return animationQueue.peek(); + } + + public @Nullable AzQueuedAnimation next() { + return animationQueue.poll(); + } + + public void clear() { + animationQueue.clear(); + } + + public boolean isEmpty() { + return animationQueue.isEmpty(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneAnimationQueueCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneAnimationQueueCache.java new file mode 100644 index 000000000..5b8232fff --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneAnimationQueueCache.java @@ -0,0 +1,61 @@ +package mod.azure.azurelib.rewrite.animation.controller; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mod.azure.azurelib.rewrite.animation.AzBoneAnimationUpdateUtil; +import mod.azure.azurelib.rewrite.animation.cache.AzBoneCache; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzBoneAnimationQueue; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; + +/** + * The AzBoneAnimationQueueCache class is responsible for managing and updating animation queues for bones. It acts as a + * cache that maps bone names to their respective animation queues, enabling efficient updates and access. + * + * @param the type of the animatable object used in the animation context + */ +public class AzBoneAnimationQueueCache { + + private final Map boneAnimationQueues; + + private final AzBoneCache boneCache; + + public AzBoneAnimationQueueCache(AzBoneCache boneCache) { + this.boneAnimationQueues = new Object2ObjectOpenHashMap<>(); + this.boneCache = boneCache; + } + + public void update(AzEasingType easingType) { + var boneSnapshots = boneCache.getBoneSnapshotsByName(); + + for (var boneAnimation : boneAnimationQueues.values()) { + var bone = boneAnimation.bone(); + var snapshot = boneSnapshots.get(bone.getName()); + var initialSnapshot = bone.getInitialAzSnapshot(); + + AzBoneAnimationUpdateUtil.updateRotations(boneAnimation, bone, easingType, initialSnapshot, snapshot); + AzBoneAnimationUpdateUtil.updatePositions(boneAnimation, bone, easingType, snapshot); + AzBoneAnimationUpdateUtil.updateScale(boneAnimation, bone, easingType, snapshot); + } + } + + public Collection values() { + return boneAnimationQueues.values(); + } + + public @Nullable AzBoneAnimationQueue getOrNull(String boneName) { + var bone = boneCache.getBakedModel().getBoneOrNull(boneName); + + if (bone == null) { + return null; + } + + return boneAnimationQueues.computeIfAbsent(boneName, $ -> new AzBoneAnimationQueue(bone)); + } + + public void clear() { + boneAnimationQueues.clear(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneSnapshotCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneSnapshotCache.java new file mode 100644 index 000000000..474db7337 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneSnapshotCache.java @@ -0,0 +1,53 @@ +package mod.azure.azurelib.rewrite.animation.controller; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation; +import mod.azure.azurelib.rewrite.model.AzBoneSnapshot; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; + +/** + * A cache system for managing and storing {@link AzBoneSnapshot} objects related to specific animations and their + * corresponding bone animations. This class is designed to optimize the retrieval and utilization of bone snapshots + * during animation interpolation (lerping).
+ *
+ * The cache works by filtering and storing relevant {@code AzBoneSnapshot} instances for a given + * {@link AzQueuedAnimation}, based on the associated bone animations defined within the animation. It ensures that only + * the required snapshots are cached, reducing redundancy and improving performance during animation processing. + */ +public class AzBoneSnapshotCache { + + private final Map boneSnapshots; + + public AzBoneSnapshotCache() { + this.boneSnapshots = new Object2ObjectOpenHashMap<>(); + } + + /** + * Cache the relevant {@link AzBoneSnapshot AzBoneSnapshots} for the current {@link AzQueuedAnimation} for animation + * lerping + * + * @param animation The {@code QueuedAnimation} to filter {@code BoneSnapshots} for + * @param snapshots The master snapshot collection to pull filter from + */ + public void put(AzQueuedAnimation animation, Collection snapshots) { + if (animation.animation().boneAnimations() == null) { + return; + } + + for (var snapshot : snapshots) { + for (var boneAnimation : animation.animation().boneAnimations()) { + if (boneAnimation.boneName().equals(snapshot.getBone().getName())) { + boneSnapshots.put(boneAnimation.boneName(), AzBoneSnapshot.copy(snapshot)); + break; + } + } + } + } + + public @Nullable AzBoneSnapshot getOrNull(String name) { + return boneSnapshots.get(name); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAbstractKeyframeExecutor.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAbstractKeyframeExecutor.java new file mode 100644 index 000000000..b20b107c5 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAbstractKeyframeExecutor.java @@ -0,0 +1,79 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import mod.azure.azurelib.core.keyframe.AnimationPoint; +import mod.azure.azurelib.core.keyframe.Keyframe; +import mod.azure.azurelib.core.keyframe.KeyframeLocation; +import mod.azure.azurelib.core.math.Constant; +import mod.azure.azurelib.core.math.IValue; +import mod.azure.azurelib.core.object.Axis; + +import java.util.List; + +/** + * AzAbstractKeyframeExecutor is a base class designed to handle animations and transitions between keyframes in a + * generic and reusable fashion. It provides the foundational logic for determining the current state of an animation + * based on the tick time and computing the animation's required values. + */ +public class AzAbstractKeyframeExecutor { + + protected AzAbstractKeyframeExecutor() {} + + /** + * Convert a {@link KeyframeLocation} to an {@link AnimationPoint} + */ + protected AzAnimationPoint getAnimationPointAtTick( + List> frames, + double tick, + boolean isRotation, + Axis axis + ) { + var location = getCurrentKeyframeLocation(frames, tick); + var currentFrame = location.keyframe(); + var startValue = currentFrame.startValue().get(); + var endValue = currentFrame.endValue().get(); + + if (isRotation) { + if (!(currentFrame.startValue() instanceof Constant)) { + startValue = Math.toRadians(startValue); + + if (axis == Axis.X || axis == Axis.Y) { + startValue *= -1; + } + } + + if (!(currentFrame.endValue() instanceof Constant)) { + endValue = Math.toRadians(endValue); + + if (axis == Axis.X || axis == Axis.Y) { + endValue *= -1; + } + } + } + + return new AzAnimationPoint(currentFrame, location.startTick(), currentFrame.length(), startValue, endValue); + } + + /** + * Returns the {@link Keyframe} relevant to the current tick time + * + * @param frames The list of {@code Keyframes} to filter through + * @param ageInTicks The current tick time + * @return A new {@code KeyframeLocation} containing the current {@code Keyframe} and the tick time used to find it + */ + protected AzKeyframeLocation> getCurrentKeyframeLocation( + List> frames, + double ageInTicks + ) { + var totalFrameTime = 0.0; + + for (var frame : frames) { + totalFrameTime += frame.length(); + + if (totalFrameTime > ageInTicks) { + return new AzKeyframeLocation<>(frame, (ageInTicks - (totalFrameTime - frame.length()))); + } + } + + return new AzKeyframeLocation<>(frames.get(frames.size() - 1), ageInTicks); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAnimationPoint.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAnimationPoint.java new file mode 100644 index 000000000..094ea1cdc --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAnimationPoint.java @@ -0,0 +1,27 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +/** + * Animation state record that holds the state of an animation at a given point + * + * @param currentTick The lerped tick time (current tick + partial tick) of the point + * @param transitionLength The length of time (in ticks) that the point should take to transition + * @param animationStartValue The start value to provide to the animation handling system + * @param animationEndValue The end value to provide to the animation handling system + * @param keyframe The {@code Nullable} Keyframe + */ +public record AzAnimationPoint( + AzKeyframe keyframe, + double currentTick, + double transitionLength, + double animationStartValue, + double animationEndValue +) { + + @Override + public String toString() { + return "Tick: " + this.currentTick + + " | Transition Length: " + this.transitionLength + + " | Start Value: " + this.animationStartValue + + " | End Value: " + this.animationEndValue; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimation.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimation.java new file mode 100644 index 000000000..b76970f1d --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimation.java @@ -0,0 +1,20 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import mod.azure.azurelib.core.keyframe.Keyframe; +import mod.azure.azurelib.core.math.IValue; + +/** + * A record of a deserialized animation for a given bone.
+ * Responsible for holding the various {@link Keyframe Keyframes} for the bone's animation transformations + * + * @param boneName The name of the bone as listed in the {@code animation.json} + * @param rotationKeyframes The deserialized rotation {@code Keyframe} stack + * @param positionKeyframes The deserialized position {@code Keyframe} stack + * @param scaleKeyframes The deserialized scale {@code Keyframe} stack + */ +public record AzBoneAnimation( + String boneName, + AzKeyframeStack> rotationKeyframes, + AzKeyframeStack> positionKeyframes, + AzKeyframeStack> scaleKeyframes +) {} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimationQueue.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimationQueue.java new file mode 100644 index 000000000..4f636ee8e --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimationQueue.java @@ -0,0 +1,400 @@ +/** + * This class is a fork of the matching class found in the Geckolib repository. Original source: + * https://github.com/bernie-g/geckolib Copyright © 2024 Bernie-G. Licensed under the MIT License. + * https://github.com/bernie-g/geckolib/blob/main/LICENSE + */ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.model.AzBoneSnapshot; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * A bone pseudo-stack for bone animation positions, scales, and rotations. Animation points are calculated then pushed + * onto their respective queues to be used for transformations in rendering + */ +public record AzBoneAnimationQueue( + AzBone bone, + Queue rotationXQueue, + Queue rotationYQueue, + Queue rotationZQueue, + Queue positionXQueue, + Queue positionYQueue, + Queue positionZQueue, + Queue scaleXQueue, + Queue scaleYQueue, + Queue scaleZQueue +) { + + public AzBoneAnimationQueue(AzBone bone) { + // TODO: Optimize + this( + bone, + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>() + ); + } + + /** + * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#positionXQueue} + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (based on the {@link AzAnimationController}) + * @param startValue The value of the point at the start of its transition + * @param endValue The value of the point at the end of its transition + */ + public void addPosXPoint( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + double startValue, + double endValue + ) { + this.positionXQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue)); + } + + /** + * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#positionYQueue} + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (based on the {@link AzAnimationController}) + * @param startValue The value of the point at the start of its transition + * @param endValue The value of the point at the end of its transition + */ + public void addPosYPoint( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + double startValue, + double endValue + ) { + this.positionYQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue)); + } + + /** + * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#positionZQueue} + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (based on the {@link AzAnimationController}) + * @param startValue The value of the point at the start of its transition + * @param endValue The value of the point at the end of its transition + */ + public void addPosZPoint( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + double startValue, + double endValue + ) { + this.positionZQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue)); + } + + /** + * Add a new X, Y, and Z position {@link AzAnimationPoint} to their respective queues + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (base on the {@link AzAnimationController} + * @param startSnapshot The {@link AzBoneSnapshot} that serves as the starting positions relevant to the keyframe + * provided + * @param nextXPoint The X {@code AnimationPoint} that is next in the queue, to serve as the end value of the + * new point + * @param nextYPoint The Y {@code AnimationPoint} that is next in the queue, to serve as the end value of the + * new point + * @param nextZPoint The Z {@code AnimationPoint} that is next in the queue, to serve as the end value of the + * new point + */ + public void addNextPosition( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + AzBoneSnapshot startSnapshot, + AzAnimationPoint nextXPoint, + AzAnimationPoint nextYPoint, + AzAnimationPoint nextZPoint + ) { + addPosXPoint( + keyframe, + lerpedTick, + transitionLength, + startSnapshot.getOffsetX(), + nextXPoint.animationStartValue() + ); + addPosYPoint( + keyframe, + lerpedTick, + transitionLength, + startSnapshot.getOffsetY(), + nextYPoint.animationStartValue() + ); + addPosZPoint( + keyframe, + lerpedTick, + transitionLength, + startSnapshot.getOffsetZ(), + nextZPoint.animationStartValue() + ); + } + + /** + * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#scaleXQueue} + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (based on the {@link AzAnimationController}) + * @param startValue The value of the point at the start of its transition + * @param endValue The value of the point at the end of its transition + */ + public void addScaleXPoint( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + double startValue, + double endValue + ) { + this.scaleXQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue)); + } + + /** + * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#scaleYQueue} + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (based on the {@link AzAnimationController}) + * @param startValue The value of the point at the start of its transition + * @param endValue The value of the point at the end of its transition + */ + public void addScaleYPoint( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + double startValue, + double endValue + ) { + this.scaleYQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue)); + } + + /** + * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#scaleZQueue} + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (based on the {@link AzAnimationController}) + * @param startValue The value of the point at the start of its transition + * @param endValue The value of the point at the end of its transition + */ + public void addScaleZPoint( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + double startValue, + double endValue + ) { + this.scaleZQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue)); + } + + /** + * Add a new X, Y, and Z scale {@link AzAnimationPoint} to their respective queues + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (base on the {@link AzAnimationController} + * @param startSnapshot The {@link AzBoneSnapshot} that serves as the starting scales relevant to the keyframe + * provided + * @param nextXPoint The X {@code AnimationPoint} that is next in the queue, to serve as the end value of the + * new point + * @param nextYPoint The Y {@code AnimationPoint} that is next in the queue, to serve as the end value of the + * new point + * @param nextZPoint The Z {@code AnimationPoint} that is next in the queue, to serve as the end value of the + * new point + */ + public void addNextScale( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + AzBoneSnapshot startSnapshot, + AzAnimationPoint nextXPoint, + AzAnimationPoint nextYPoint, + AzAnimationPoint nextZPoint + ) { + addScaleXPoint( + keyframe, + lerpedTick, + transitionLength, + startSnapshot.getScaleX(), + nextXPoint.animationStartValue() + ); + addScaleYPoint( + keyframe, + lerpedTick, + transitionLength, + startSnapshot.getScaleY(), + nextYPoint.animationStartValue() + ); + addScaleZPoint( + keyframe, + lerpedTick, + transitionLength, + startSnapshot.getScaleZ(), + nextZPoint.animationStartValue() + ); + } + + /** + * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#rotationXQueue} + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (based on the {@link AzAnimationController}) + * @param startValue The value of the point at the start of its transition + * @param endValue The value of the point at the end of its transition + */ + public void addRotationXPoint( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + double startValue, + double endValue + ) { + this.rotationXQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue)); + } + + /** + * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#rotationYQueue} + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (based on the {@link AzAnimationController}) + * @param startValue The value of the point at the start of its transition + * @param endValue The value of the point at the end of its transition + */ + public void addRotationYPoint( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + double startValue, + double endValue + ) { + this.rotationYQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue)); + } + + /** + * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#rotationZQueue} + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (based on the {@link AzAnimationController}) + * @param startValue The value of the point at the start of its transition + * @param endValue The value of the point at the end of its transition + */ + public void addRotationZPoint( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + double startValue, + double endValue + ) { + this.rotationZQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue)); + } + + /** + * Add a new X, Y, and Z scale {@link AzAnimationPoint} to their respective queues + * + * @param keyframe The {@code Nullable} Keyframe relevant to the animation point + * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at + * @param transitionLength The length of the transition (base on the {@link AzAnimationController} + * @param startSnapshot The {@link AzBoneSnapshot} that serves as the starting rotations relevant to the keyframe + * provided + * @param initialSnapshot The {@link AzBoneSnapshot} that serves as the unmodified rotations of the bone + * @param nextXPoint The X {@code AnimationPoint} that is next in the queue, to serve as the end value of the + * new point + * @param nextYPoint The Y {@code AnimationPoint} that is next in the queue, to serve as the end value of the + * new point + * @param nextZPoint The Z {@code AnimationPoint} that is next in the queue, to serve as the end value of the + * new point + */ + public void addNextRotation( + AzKeyframe keyframe, + double lerpedTick, + double transitionLength, + AzBoneSnapshot startSnapshot, + AzBoneSnapshot initialSnapshot, + AzAnimationPoint nextXPoint, + AzAnimationPoint nextYPoint, + AzAnimationPoint nextZPoint + ) { + addRotationXPoint( + keyframe, + lerpedTick, + transitionLength, + startSnapshot.getRotX() - initialSnapshot.getRotX(), + nextXPoint.animationStartValue() + ); + addRotationYPoint( + keyframe, + lerpedTick, + transitionLength, + startSnapshot.getRotY() - initialSnapshot.getRotY(), + nextYPoint.animationStartValue() + ); + addRotationZPoint( + keyframe, + lerpedTick, + transitionLength, + startSnapshot.getRotZ() - initialSnapshot.getRotZ(), + nextZPoint.animationStartValue() + ); + } + + /** + * Add an X, Y, and Z position {@link AzAnimationPoint} to their respective queues + * + * @param xPoint The x position {@code AnimationPoint} to add + * @param yPoint The y position {@code AnimationPoint} to add + * @param zPoint The z position {@code AnimationPoint} to add + */ + public void addPositions(AzAnimationPoint xPoint, AzAnimationPoint yPoint, AzAnimationPoint zPoint) { + this.positionXQueue.add(xPoint); + this.positionYQueue.add(yPoint); + this.positionZQueue.add(zPoint); + } + + /** + * Add an X, Y, and Z scale {@link AzAnimationPoint} to their respective queues + * + * @param xPoint The x scale {@code AnimationPoint} to add + * @param yPoint The y scale {@code AnimationPoint} to add + * @param zPoint The z scale {@code AnimationPoint} to add + */ + public void addScales(AzAnimationPoint xPoint, AzAnimationPoint yPoint, AzAnimationPoint zPoint) { + this.scaleXQueue.add(xPoint); + this.scaleYQueue.add(yPoint); + this.scaleZQueue.add(zPoint); + } + + /** + * Add an X, Y, and Z rotation {@link AzAnimationPoint} to their respective queues + * + * @param xPoint The x rotation {@code AnimationPoint} to add + * @param yPoint The y rotation {@code AnimationPoint} to add + * @param zPoint The z rotation {@code AnimationPoint} to add + */ + public void addRotations(AzAnimationPoint xPoint, AzAnimationPoint yPoint, AzAnimationPoint zPoint) { + this.rotationXQueue.add(xPoint); + this.rotationYQueue.add(yPoint); + this.rotationZQueue.add(zPoint); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframe.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframe.java new file mode 100644 index 000000000..e03b4943f --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframe.java @@ -0,0 +1,56 @@ +/** + * This class is a fork of the matching class found in the Geckolib repository. Original source: + * https://github.com/bernie-g/geckolib Copyright © 2024 Bernie-G. Licensed under the MIT License. + * https://github.com/bernie-g/geckolib/blob/main/LICENSE + */ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import mod.azure.azurelib.core.math.IValue; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypes; + +import java.util.List; +import java.util.Objects; + +/** + * Animation keyframe data + * + * @param length The length (in ticks) the keyframe lasts for + * @param startValue The value to start the keyframe's transformation with + * @param endValue The value to end the keyframe's transformation with + * @param easingType The {@code EasingType} to use for transformations + * @param easingArgs The arguments to provide to the easing calculation + */ +public record AzKeyframe( + double length, + T startValue, + T endValue, + AzEasingType easingType, + List easingArgs +) { + + public AzKeyframe(double length, T startValue, T endValue) { + this(length, startValue, endValue, AzEasingTypes.LINEAR); + } + + public AzKeyframe(double length, T startValue, T endValue, AzEasingType easingType) { + this(length, startValue, endValue, easingType, new ObjectArrayList<>(0)); + } + + @Override + public int hashCode() { + return Objects.hash(this.length, this.startValue, this.endValue, this.easingType, this.easingArgs); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (obj == null || getClass() != obj.getClass()) + return false; + + return hashCode() == obj.hashCode(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbackHandler.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbackHandler.java new file mode 100644 index 000000000..70fb79e6f --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbackHandler.java @@ -0,0 +1,126 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import mod.azure.azurelib.core.keyframe.event.data.KeyFrameData; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.event.AzCustomInstructionKeyframeEvent; +import mod.azure.azurelib.rewrite.animation.event.AzParticleKeyframeEvent; +import mod.azure.azurelib.rewrite.animation.event.AzSoundKeyframeEvent; +import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +/** + * AzKeyframeCallbackHandler acts as a handler for managing animation keyframe events such as sound, particle, or custom + * events during a specific animation. It works in conjunction with an animation controller and a set of keyframe + * callbacks, executing them as appropriate based on the animation's progress.
+ * This class is generic and operates on a user-defined animatable type to handle various keyframe events related to + * animations. + * + * @param the type of the animatable object being handled + */ +// TODO: reduce the boilerplate of the specialized handle functions in this class. +public class AzKeyframeCallbackHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AzKeyframeCallbackHandler.class); + + private final AzAnimationController animationController; + + private final Set executedKeyframes; + + private final AzKeyframeCallbacks keyframeCallbacks; + + public AzKeyframeCallbackHandler( + AzAnimationController animationController, + AzKeyframeCallbacks keyframeCallbacks + ) { + this.animationController = animationController; + this.executedKeyframes = new ObjectOpenHashSet<>(); + this.keyframeCallbacks = keyframeCallbacks; + } + + public void handle(T animatable, double adjustedTick) { + handleSoundKeyframes(animatable, adjustedTick); + handleParticleKeyframes(animatable, adjustedTick); + handleCustomKeyframes(animatable, adjustedTick); + } + + private void handleCustomKeyframes(T animatable, double adjustedTick) { + var customKeyframeHandler = keyframeCallbacks.customKeyframeHandler(); + var customInstructions = currentAnimation().animation().keyframes().customInstructions(); + + for (var keyframeData : customInstructions) { + if (adjustedTick >= keyframeData.getStartTick() && executedKeyframes.add(keyframeData)) { + if (customKeyframeHandler == null) { + LOGGER.warn( + "Custom Instruction Keyframe found for {} -> {}, but no keyframe handler registered", + animatable.getClass().getSimpleName(), + animationController.name() + ); + break; + } + + customKeyframeHandler.handle( + new AzCustomInstructionKeyframeEvent<>(animatable, adjustedTick, animationController, keyframeData) + ); + } + } + } + + private void handleParticleKeyframes(T animatable, double adjustedTick) { + var particleKeyframeHandler = keyframeCallbacks.particleKeyframeHandler(); + var particleInstructions = currentAnimation().animation().keyframes().particles(); + + for (var keyframeData : particleInstructions) { + if (adjustedTick >= keyframeData.getStartTick() && executedKeyframes.add(keyframeData)) { + if (particleKeyframeHandler == null) { + LOGGER.warn( + "Particle Keyframe found for {} -> {}, but no keyframe handler registered", + animatable.getClass().getSimpleName(), + animationController.name() + ); + break; + } + + particleKeyframeHandler.handle( + new AzParticleKeyframeEvent<>(animatable, adjustedTick, animationController, keyframeData) + ); + } + } + } + + private void handleSoundKeyframes(T animatable, double adjustedTick) { + var soundKeyframeHandler = keyframeCallbacks.soundKeyframeHandler(); + var soundInstructions = currentAnimation().animation().keyframes().sounds(); + + for (var keyframeData : soundInstructions) { + if (adjustedTick >= keyframeData.getStartTick() && executedKeyframes.add(keyframeData)) { + if (soundKeyframeHandler == null) { + LOGGER.warn( + "Sound Keyframe found for {} -> {}, but no keyframe handler registered", + animatable.getClass().getSimpleName(), + animationController.name() + ); + break; + } + + soundKeyframeHandler.handle( + new AzSoundKeyframeEvent<>(animatable, adjustedTick, animationController, keyframeData) + ); + } + } + } + + /** + * Clear the {@link KeyFrameData} cache in preparation for the next animation + */ + public void reset() { + executedKeyframes.clear(); + } + + private AzQueuedAnimation currentAnimation() { + return animationController.currentAnimation(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbacks.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbacks.java new file mode 100644 index 000000000..342795f97 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbacks.java @@ -0,0 +1,105 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import mod.azure.azurelib.rewrite.animation.controller.keyframe.handler.AzCustomKeyframeHandler; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.handler.AzParticleKeyframeHandler; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.handler.AzSoundKeyframeHandler; +import mod.azure.azurelib.rewrite.animation.event.AzCustomInstructionKeyframeEvent; +import mod.azure.azurelib.rewrite.animation.event.AzParticleKeyframeEvent; +import mod.azure.azurelib.rewrite.animation.event.AzSoundKeyframeEvent; +import org.jetbrains.annotations.Nullable; + +/** + * The AzKeyframeCallbacks class manages callbacks for different types of keyframe events, enabling the handling of + * sound, particle, and custom-defined keyframe instructions during an animation sequence. + * + * @param The type of entity or object this keyframe callback interacts with. + */ +public class AzKeyframeCallbacks { + + private static final AzKeyframeCallbacks NO_OP = new AzKeyframeCallbacks<>(null, null, null); + + @SuppressWarnings("unchecked") + public static AzKeyframeCallbacks noop() { + return (AzKeyframeCallbacks) NO_OP; + } + + private final @Nullable AzCustomKeyframeHandler customKeyframeHandler; + + private final @Nullable AzParticleKeyframeHandler particleKeyframeHandler; + + private final @Nullable AzSoundKeyframeHandler soundKeyframeHandler; + + private AzKeyframeCallbacks( + @Nullable AzCustomKeyframeHandler customKeyframeHandler, + @Nullable AzParticleKeyframeHandler particleKeyframeHandler, + @Nullable AzSoundKeyframeHandler soundKeyframeHandler + ) { + this.customKeyframeHandler = customKeyframeHandler; + this.particleKeyframeHandler = particleKeyframeHandler; + this.soundKeyframeHandler = soundKeyframeHandler; + } + + public @Nullable AzCustomKeyframeHandler customKeyframeHandler() { + return customKeyframeHandler; + } + + public @Nullable AzParticleKeyframeHandler particleKeyframeHandler() { + return particleKeyframeHandler; + } + + public @Nullable AzSoundKeyframeHandler soundKeyframeHandler() { + return soundKeyframeHandler; + } + + public static Builder builder() { + return new Builder<>(); + } + + public static class Builder { + + private @Nullable AzCustomKeyframeHandler customKeyframeHandler; + + private @Nullable AzParticleKeyframeHandler particleKeyframeHandler; + + private @Nullable AzSoundKeyframeHandler soundKeyframeHandler; + + private Builder() {} + + /** + * Applies the given {@link AzSoundKeyframeHandler} to this controller, for handling {@link AzSoundKeyframeEvent + * sound keyframe instructions}. + * + * @return this + */ + public Builder setSoundKeyframeHandler(AzSoundKeyframeHandler soundHandler) { + this.soundKeyframeHandler = soundHandler; + return this; + } + + /** + * Applies the given {@link AzParticleKeyframeHandler} to this controller, for handling + * {@link AzParticleKeyframeEvent particle keyframe instructions}. + * + * @return this + */ + public Builder setParticleKeyframeHandler(AzParticleKeyframeHandler particleHandler) { + this.particleKeyframeHandler = particleHandler; + return this; + } + + /** + * Applies the given {@link AzCustomKeyframeHandler} to this controller, for handling + * {@link AzCustomInstructionKeyframeEvent sound keyframe instructions}. + * + * @return this + */ + public Builder setCustomInstructionKeyframeHandler(AzCustomKeyframeHandler customInstructionHandler) { + this.customKeyframeHandler = customInstructionHandler; + return this; + } + + public AzKeyframeCallbacks build() { + return new AzKeyframeCallbacks<>(customKeyframeHandler, particleKeyframeHandler, soundKeyframeHandler); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeExecutor.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeExecutor.java new file mode 100644 index 000000000..4fea0b0ef --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeExecutor.java @@ -0,0 +1,126 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import mod.azure.azurelib.core.math.IValue; +import mod.azure.azurelib.core.molang.MolangParser; +import mod.azure.azurelib.core.molang.MolangQueries; +import mod.azure.azurelib.core.object.Axis; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.AzBoneAnimationQueueCache; +import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation; +import org.jetbrains.annotations.NotNull; + +import java.util.NoSuchElementException; + +/** + * AzKeyframeExecutor is a specialized implementation of {@link AzAbstractKeyframeExecutor}, designed to handle + * keyframe-based animations for animatable objects. It delegates animation control to an {@link AzAnimationController} + * and manages bone animation queues through an {@link AzBoneAnimationQueueCache}.
+ * This class processes and applies transformations such as rotation, position, and scale to bone animations, based on + * the current tick time and the keyframes associated with each bone animation. + * + * @param The type of the animatable object to which the keyframe animations will be applied + */ +public class AzKeyframeExecutor extends AzAbstractKeyframeExecutor { + + private final AzAnimationController animationController; + + private final AzBoneAnimationQueueCache boneAnimationQueueCache; + + public AzKeyframeExecutor( + AzAnimationController animationController, + AzBoneAnimationQueueCache boneAnimationQueueCache + ) { + this.animationController = animationController; + this.boneAnimationQueueCache = boneAnimationQueueCache; + } + + /** + * Handle the current animation's state modifications and translations + * + * @param crashWhenCantFindBone Whether the controller should throw an exception when unable to find the required + * bone, or continue with the remaining bones + */ + public void execute(@NotNull AzQueuedAnimation currentAnimation, T animatable, boolean crashWhenCantFindBone) { + var keyframeCallbackHandler = animationController.keyframeManager().keyframeCallbackHandler(); + var controllerTimer = animationController.controllerTimer(); + var transitionLength = animationController.animationProperties().transitionLength(); + + final double finalAdjustedTick = controllerTimer.getAdjustedTick(); + + MolangParser.INSTANCE.setMemoizedValue(MolangQueries.ANIM_TIME, () -> finalAdjustedTick / 20d); + + for (var boneAnimation : currentAnimation.animation().boneAnimations()) { + var boneAnimationQueue = boneAnimationQueueCache.getOrNull(boneAnimation.boneName()); + + if (boneAnimationQueue == null) { + if (crashWhenCantFindBone) { + throw new NoSuchElementException("Could not find bone: " + boneAnimation.boneName()); + } + + continue; + } + + var rotationKeyframes = boneAnimation.rotationKeyframes(); + var positionKeyframes = boneAnimation.positionKeyframes(); + var scaleKeyframes = boneAnimation.scaleKeyframes(); + var adjustedTick = controllerTimer.getAdjustedTick(); + + updateRotation(rotationKeyframes, boneAnimationQueue, adjustedTick); + updatePosition(positionKeyframes, boneAnimationQueue, adjustedTick); + updateScale(scaleKeyframes, boneAnimationQueue, adjustedTick); + } + + // TODO: Is this correct??? + controllerTimer.addToAdjustedTick(transitionLength); + + keyframeCallbackHandler.handle(animatable, controllerTimer.getAdjustedTick()); + } + + private void updateRotation( + AzKeyframeStack> keyframes, + AzBoneAnimationQueue queue, + double adjustedTick + ) { + if (keyframes.xKeyframes().isEmpty()) { + return; + } + + var x = getAnimationPointAtTick(keyframes.xKeyframes(), adjustedTick, true, Axis.X); + var y = getAnimationPointAtTick(keyframes.yKeyframes(), adjustedTick, true, Axis.Y); + var z = getAnimationPointAtTick(keyframes.zKeyframes(), adjustedTick, true, Axis.Z); + + queue.addRotations(x, y, z); + } + + private void updatePosition( + AzKeyframeStack> keyframes, + AzBoneAnimationQueue queue, + double adjustedTick + ) { + if (keyframes.xKeyframes().isEmpty()) { + return; + } + + var x = getAnimationPointAtTick(keyframes.xKeyframes(), adjustedTick, false, Axis.X); + var y = getAnimationPointAtTick(keyframes.yKeyframes(), adjustedTick, false, Axis.Y); + var z = getAnimationPointAtTick(keyframes.zKeyframes(), adjustedTick, false, Axis.Z); + + queue.addPositions(x, y, z); + } + + private void updateScale( + AzKeyframeStack> keyframes, + AzBoneAnimationQueue queue, + double adjustedTick + ) { + if (keyframes.xKeyframes().isEmpty()) { + return; + } + + var x = getAnimationPointAtTick(keyframes.xKeyframes(), adjustedTick, false, Axis.X); + var y = getAnimationPointAtTick(keyframes.yKeyframes(), adjustedTick, false, Axis.Y); + var z = getAnimationPointAtTick(keyframes.zKeyframes(), adjustedTick, false, Axis.Z); + + queue.addScales(x, y, z); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeLocation.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeLocation.java new file mode 100644 index 000000000..13858caa0 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeLocation.java @@ -0,0 +1,14 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import mod.azure.azurelib.core.keyframe.Keyframe; + +/** + * A named pair object that stores a {@link Keyframe} and a double representing a temporally placed {@code Keyframe} + * + * @param keyframe The {@code Keyframe} at the tick time + * @param startTick The animation tick time at the start of this {@code Keyframe} + */ +public record AzKeyframeLocation>( + T keyframe, + double startTick +) {} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeManager.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeManager.java new file mode 100644 index 000000000..3c1da1ae4 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeManager.java @@ -0,0 +1,47 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.AzBoneAnimationQueueCache; +import mod.azure.azurelib.rewrite.animation.controller.AzBoneSnapshotCache; + +/** + * AzKeyframeManager is responsible for managing the keyframe-related operations in an animation system. It coordinates + * the execution, transition, and callback handling of animation keyframes through its associated components. + * + * @param the type of the animatable object being handled + */ +public class AzKeyframeManager { + + private final AzKeyframeCallbackHandler keyframeCallbackHandler; + + private final AzKeyframeExecutor keyframeExecutor; + + private final AzKeyframeTransitioner keyframeTransitioner; + + public AzKeyframeManager( + AzAnimationController animationController, + AzBoneAnimationQueueCache boneAnimationQueueCache, + AzBoneSnapshotCache boneSnapshotCache, + AzKeyframeCallbacks keyframeCallbacks + ) { + this.keyframeCallbackHandler = new AzKeyframeCallbackHandler<>(animationController, keyframeCallbacks); + this.keyframeExecutor = new AzKeyframeExecutor<>(animationController, boneAnimationQueueCache); + this.keyframeTransitioner = new AzKeyframeTransitioner<>( + animationController, + boneAnimationQueueCache, + boneSnapshotCache + ); + } + + public AzKeyframeCallbackHandler keyframeCallbackHandler() { + return keyframeCallbackHandler; + } + + public AzKeyframeExecutor keyframeExecutor() { + return keyframeExecutor; + } + + public AzKeyframeTransitioner keyframeTransitioner() { + return keyframeTransitioner; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeStack.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeStack.java new file mode 100644 index 000000000..f1fba098f --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeStack.java @@ -0,0 +1,43 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import java.util.List; + +/** + * Stores a triplet of {@link AzKeyframe Keyframes} in an ordered stack + */ +public record AzKeyframeStack>( + List xKeyframes, + List yKeyframes, + List zKeyframes +) { + + public AzKeyframeStack() { + this(new ObjectArrayList<>(), new ObjectArrayList<>(), new ObjectArrayList<>()); + } + + public static > AzKeyframeStack from(AzKeyframeStack otherStack) { + return new AzKeyframeStack<>(otherStack.xKeyframes, otherStack.yKeyframes, otherStack.zKeyframes); + } + + public double getLastKeyframeTime() { + double xTime = 0; + double yTime = 0; + double zTime = 0; + + for (T frame : xKeyframes()) { + xTime += frame.length(); + } + + for (T frame : yKeyframes()) { + yTime += frame.length(); + } + + for (T frame : zKeyframes()) { + zTime += frame.length(); + } + + return Math.max(xTime, Math.max(yTime, zTime)); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeTransitioner.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeTransitioner.java new file mode 100644 index 000000000..d8a6b2971 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeTransitioner.java @@ -0,0 +1,125 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe; + +import mod.azure.azurelib.core.math.IValue; +import mod.azure.azurelib.core.molang.MolangParser; +import mod.azure.azurelib.core.molang.MolangQueries; +import mod.azure.azurelib.core.object.Axis; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.AzBoneAnimationQueueCache; +import mod.azure.azurelib.rewrite.animation.controller.AzBoneSnapshotCache; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.model.AzBoneSnapshot; + +import java.util.Map; +import java.util.NoSuchElementException; + +/** + * AzKeyframeTransitioner is a specialized class for executing smooth animations and transitions between keyframes for + * bones in an animation system. It utilizes animation controllers, bone animation queue caches, and bone snapshot + * caches to manage and apply transitions for rotation, position, and scale of bones. + * + * @param The type of the animation data handled by the associated animation controller. + */ +public class AzKeyframeTransitioner extends AzAbstractKeyframeExecutor { + + private final AzAnimationController animationController; + + private final AzBoneAnimationQueueCache boneAnimationQueueCache; + + private final AzBoneSnapshotCache boneSnapshotCache; + + public AzKeyframeTransitioner( + AzAnimationController animationController, + AzBoneAnimationQueueCache boneAnimationQueueCache, + AzBoneSnapshotCache boneSnapshotCache + ) { + this.animationController = animationController; + this.boneAnimationQueueCache = boneAnimationQueueCache; + this.boneSnapshotCache = boneSnapshotCache; + } + + public void transition(Map bones, boolean crashWhenCantFindBone, double adjustedTick) { + var currentAnimation = animationController.currentAnimation(); + var transitionLength = animationController.animationProperties().transitionLength(); + + MolangParser.INSTANCE.setValue(MolangQueries.ANIM_TIME, () -> 0); + + for (var boneAnimation : currentAnimation.animation().boneAnimations()) { + var bone = bones.get(boneAnimation.boneName()); + + if (bone == null) { + if (crashWhenCantFindBone) + throw new NoSuchElementException("Could not find bone: " + boneAnimation.boneName()); + + continue; + } + + var queue = boneAnimationQueueCache.getOrNull(boneAnimation.boneName()); + var snapshot = boneSnapshotCache.getOrNull(boneAnimation.boneName()); + + var rotationKeyframes = boneAnimation.rotationKeyframes(); + var positionKeyframes = boneAnimation.positionKeyframes(); + var scaleKeyframes = boneAnimation.scaleKeyframes(); + + transitionRotation(adjustedTick, rotationKeyframes, queue, transitionLength, snapshot, bone); + transitionPosition(adjustedTick, positionKeyframes, queue, transitionLength, snapshot); + transitionScale(adjustedTick, scaleKeyframes, queue, transitionLength, snapshot); + } + } + + private void transitionRotation( + double adjustedTick, + AzKeyframeStack> keyframes, + AzBoneAnimationQueue queue, + double transitionLength, + AzBoneSnapshot snapshot, + AzBone bone + ) { + if (keyframes.xKeyframes().isEmpty()) { + return; + } + + var initialSnapshot = bone.getInitialAzSnapshot(); + var x = getAnimationPointAtTick(keyframes.xKeyframes(), 0, true, Axis.X); + var y = getAnimationPointAtTick(keyframes.yKeyframes(), 0, true, Axis.Y); + var z = getAnimationPointAtTick(keyframes.zKeyframes(), 0, true, Axis.Z); + + queue.addNextRotation(null, adjustedTick, transitionLength, snapshot, initialSnapshot, x, y, z); + } + + private void transitionPosition( + double adjustedTick, + AzKeyframeStack> keyframes, + AzBoneAnimationQueue queue, + double transitionLength, + AzBoneSnapshot snapshot + ) { + if (keyframes.xKeyframes().isEmpty()) { + return; + } + + var x = getAnimationPointAtTick(keyframes.xKeyframes(), 0, false, Axis.X); + var y = getAnimationPointAtTick(keyframes.yKeyframes(), 0, false, Axis.Y); + var z = getAnimationPointAtTick(keyframes.zKeyframes(), 0, false, Axis.Z); + + queue.addNextPosition(null, adjustedTick, transitionLength, snapshot, x, y, z); + } + + private void transitionScale( + double adjustedTick, + AzKeyframeStack> keyframes, + AzBoneAnimationQueue queue, + double transitionLength, + AzBoneSnapshot snapshot + ) { + if (keyframes.xKeyframes().isEmpty()) { + return; + } + + var x = getAnimationPointAtTick(keyframes.xKeyframes(), 0, false, Axis.X); + var y = getAnimationPointAtTick(keyframes.yKeyframes(), 0, false, Axis.Y); + var z = getAnimationPointAtTick(keyframes.zKeyframes(), 0, false, Axis.Z); + + queue.addNextScale(null, adjustedTick, transitionLength, snapshot, x, y, z); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzCustomKeyframeHandler.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzCustomKeyframeHandler.java new file mode 100644 index 000000000..81041d535 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzCustomKeyframeHandler.java @@ -0,0 +1,14 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe.handler; + +import mod.azure.azurelib.rewrite.animation.event.AzCustomInstructionKeyframeEvent; + +/** + * A handler for pre-defined custom instruction keyframes. When the keyframe is encountered, the + * {@link AzCustomKeyframeHandler#handle(AzCustomInstructionKeyframeEvent)} method will be called. You can then take + * whatever action you want at this point. + */ +@FunctionalInterface +public interface AzCustomKeyframeHandler { + + void handle(AzCustomInstructionKeyframeEvent event); +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzParticleKeyframeHandler.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzParticleKeyframeHandler.java new file mode 100644 index 000000000..5e3c27068 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzParticleKeyframeHandler.java @@ -0,0 +1,14 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe.handler; + +import mod.azure.azurelib.rewrite.animation.event.AzParticleKeyframeEvent; + +/** + * A handler for when a predefined particle keyframe is hit. When the keyframe is encountered, the + * {@link AzParticleKeyframeHandler#handle(AzParticleKeyframeEvent)} method will be called. Spawn the particles/effects + * of your choice at this time. + */ +@FunctionalInterface +public interface AzParticleKeyframeHandler { + + void handle(AzParticleKeyframeEvent event); +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzSoundKeyframeHandler.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzSoundKeyframeHandler.java new file mode 100644 index 000000000..e6d751e0c --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzSoundKeyframeHandler.java @@ -0,0 +1,14 @@ +package mod.azure.azurelib.rewrite.animation.controller.keyframe.handler; + +import mod.azure.azurelib.rewrite.animation.event.AzSoundKeyframeEvent; + +/** + * A handler for when a predefined sound keyframe is hit. When the keyframe is encountered, the + * {@link AzSoundKeyframeHandler#handle(AzSoundKeyframeEvent)} method will be called. Play the sound(s) of your choice + * at this time. + */ +@FunctionalInterface +public interface AzSoundKeyframeHandler { + + void handle(AzSoundKeyframeEvent event); +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/AzAnimationState.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/AzAnimationState.java new file mode 100644 index 000000000..5630454fb --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/AzAnimationState.java @@ -0,0 +1,41 @@ +package mod.azure.azurelib.rewrite.animation.controller.state; + +import mod.azure.azurelib.rewrite.animation.controller.state.machine.AzAnimationControllerStateMachine; +import mod.azure.azurelib.rewrite.util.state.State; + +/** + * Represents an abstract animation state within the {@link AzAnimationControllerStateMachine}. Each concrete + * implementation of this class defines specific behavior for managing animations during state transitions.
+ *
+ * The animation state lifecycle consists of three primary methods: + *
    + *
  • {@code onEnter}: Invoked when the state is entered. This method is used for initializing the state.
  • + *
  • {@code onUpdate}: Should be implemented by subclasses to define the behavior during the state's execution.
  • + *
  • {@code onExit}: Invoked when transitioning out of the state. This method is used for cleanup or + * finalization.
  • + *
+ * + * @param the type of the animation context associated with this state + */ +public abstract class AzAnimationState implements State> { + + private boolean isActive; + + protected AzAnimationState() { + this.isActive = false; + } + + @Override + public void onEnter(AzAnimationControllerStateMachine.Context context) { + this.isActive = true; + } + + public boolean isActive() { + return isActive; + } + + @Override + public void onExit(AzAnimationControllerStateMachine.Context context) { + this.isActive = false; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPauseState.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPauseState.java new file mode 100644 index 000000000..537acc79a --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPauseState.java @@ -0,0 +1,25 @@ +package mod.azure.azurelib.rewrite.animation.controller.state.impl; + +import mod.azure.azurelib.rewrite.animation.controller.state.machine.AzAnimationControllerStateMachine; + +/** + * Represents the paused state in the animation state machine. This state ensures no updates are applied to the + * animation while it is paused, maintaining its current state until it is transitioned back to a play or stop state. + * + * @param the type of animation managed by the state + */ +public final class AzAnimationPauseState extends AzAnimationPlayState { + + public AzAnimationPauseState() {} + + @Override + public void onEnter(AzAnimationControllerStateMachine.Context context) { + // Do nothing, because the pause state shouldn't reset on enter. + } + + @Override + public void onUpdate(AzAnimationControllerStateMachine.Context context) { + super.onUpdate(context); + // Pause state does not need to do anything. + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPlayState.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPlayState.java new file mode 100644 index 000000000..b9eabc93c --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPlayState.java @@ -0,0 +1,88 @@ +package mod.azure.azurelib.rewrite.animation.controller.state.impl; + +import mod.azure.azurelib.rewrite.animation.controller.state.AzAnimationState; +import mod.azure.azurelib.rewrite.animation.controller.state.machine.AzAnimationControllerStateMachine; + +/** + * Represents a "play" state in an animation state machine. This state is responsible for managing the playing of + * animations either by starting from the beginning or playing subsequent animations. It ensures that the animation + * progresses based on the controller's timer and handles transitions when animations complete.
+ *
+ * Inherits general animation state behavior such as lifecycle management from {@link AzAnimationState}. + * + * @param the type of animation being managed + */ +public class AzAnimationPlayState extends AzAnimationState { + + public AzAnimationPlayState() {} + + @Override + public void onEnter(AzAnimationControllerStateMachine.Context context) { + super.onEnter(context); + var controller = context.animationController(); + var controllerTimer = controller.controllerTimer(); + + controllerTimer.reset(); + } + + @Override + public void onUpdate(AzAnimationControllerStateMachine.Context context) { + var controller = context.animationController(); + var controllerTimer = controller.controllerTimer(); + var currentAnimation = controller.currentAnimation(); + + if (currentAnimation == null) { + // If the current animation is null, we should try to play the next animation. + tryPlayNextOrStop(context); + return; + } + + currentAnimation.playBehavior().onUpdate(context); + + // At this point we have an animation currently playing. We need to query if that animation has finished. + + var animContext = context.animationContext(); + var animatable = animContext.animatable(); + var hasAnimationFinished = controllerTimer.getAdjustedTick() >= currentAnimation.animation().length(); + + if (hasAnimationFinished) { + currentAnimation.playBehavior().onFinish(context); + } + + if (context.stateMachine().isStopped()) { + // Nothing more to do at this point since we can't play the animation again, so return. + return; + } + + // The animation is still running at this point, proceed with updating the bones according to keyframes. + + var keyframeManager = controller.keyframeManager(); + var keyframeExecutor = keyframeManager.keyframeExecutor(); + var crashWhenCantFindBone = animContext.config().crashIfBoneMissing(); + + keyframeExecutor.execute(currentAnimation, animatable, crashWhenCantFindBone); + } + + private void tryPlayNextOrStop(AzAnimationControllerStateMachine.Context context) { + var controller = context.animationController(); + var stateMachine = context.stateMachine(); + var keyframeManager = controller.keyframeManager(); + var keyframeCallbackHandler = keyframeManager.keyframeCallbackHandler(); + + keyframeCallbackHandler.reset(); + + var animationQueue = controller.animationQueue(); + var nextAnimation = animationQueue.peek(); + + if (nextAnimation == null) { + // If we can't play the next animation for some reason, then there's nothing to play. + // So we should put the state machine in the 'stop' state. + stateMachine.stop(); + return; + } + + // If we can play the next animation successfully, then let's do that. + stateMachine.transition(); + controller.setCurrentAnimation(nextAnimation); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationStopState.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationStopState.java new file mode 100644 index 000000000..6c7d4271a --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationStopState.java @@ -0,0 +1,25 @@ +package mod.azure.azurelib.rewrite.animation.controller.state.impl; + +import mod.azure.azurelib.rewrite.animation.controller.state.AzAnimationState; +import mod.azure.azurelib.rewrite.animation.controller.state.machine.AzAnimationControllerStateMachine; + +/** + * Represents the "stop" state in an animation state machine. This state is responsible for halting any ongoing + * animations and putting the animation controller into a minimal responsibility state where no further updates or + * actions are performed until a new state transition occurs.
+ *
+ * This state is typically used when an animation sequence has fully completed and no
+ *
+ * Inherits the general animation state behavior and lifecycle from {@link AzAnimationState}. + * + * @param the type of animation context associated with the state machine + */ +public final class AzAnimationStopState extends AzAnimationState { + + public AzAnimationStopState() {} + + @Override + public void onUpdate(AzAnimationControllerStateMachine.Context context) { + // Stop state does not need to do anything. + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationTransitionState.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationTransitionState.java new file mode 100644 index 000000000..c7789825a --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationTransitionState.java @@ -0,0 +1,74 @@ +package mod.azure.azurelib.rewrite.animation.controller.state.impl; + +import mod.azure.azurelib.rewrite.animation.controller.state.AzAnimationState; +import mod.azure.azurelib.rewrite.animation.controller.state.machine.AzAnimationControllerStateMachine; + +/** + * Represents a transition state in an animation state machine. This state is responsible for managing the transition + * between animations, including handling setup, updates, and transitioning to the appropriate play state when the + * transition is complete. The `AzAnimationTransitionState` extends the functionality of `AzAnimationState` to implement + * the behavior specific to transitioning between animations. It resets timers, initializes animations, and updates + * keyframes to create smooth transitions. + * + * @param the type of the animation context associated with this state + */ +public final class AzAnimationTransitionState extends AzAnimationState { + + public AzAnimationTransitionState() {} + + @Override + public void onEnter(AzAnimationControllerStateMachine.Context context) { + super.onEnter(context); + prepareTransition(context); + } + + @Override + public void onUpdate(AzAnimationControllerStateMachine.Context context) { + var controller = context.animationController(); + var controllerTimer = controller.controllerTimer(); + var animContext = context.animationContext(); + + var stateMachine = context.stateMachine(); + var boneCache = animContext.boneCache(); + + var transitionLength = controller.animationProperties().transitionLength(); + var hasFinishedTransitioning = controllerTimer.getAdjustedTick() >= transitionLength; + + if (hasFinishedTransitioning) { + // If we've exceeded the amount of time we should be transitioning, then switch to play state. + stateMachine.play(); + return; + } + + if (controller.currentAnimation() != null) { + var bones = boneCache.getBakedModel().getBonesByName(); + var crashWhenCantFindBone = animContext.config().crashIfBoneMissing(); + var keyframeTransitioner = controller.keyframeManager().keyframeTransitioner(); + + keyframeTransitioner.transition(bones, crashWhenCantFindBone, controllerTimer.getAdjustedTick()); + } + } + + private void prepareTransition(AzAnimationControllerStateMachine.Context context) { + var animContext = context.animationContext(); + var boneCache = animContext.boneCache(); + var controller = context.animationController(); + var boneSnapshotCache = controller.boneSnapshotCache(); + var controllerTimer = controller.controllerTimer(); + + controllerTimer.reset(); + controller.keyframeManager().keyframeCallbackHandler().reset(); + + var nextAnimation = controller.animationQueue().next(); + + if (nextAnimation == null) { + return; + } + + controller.setCurrentAnimation(nextAnimation); + + var snapshots = boneCache.getBoneSnapshotsByName(); + + boneSnapshotCache.put(nextAnimation, snapshots.values()); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/machine/AzAnimationControllerStateMachine.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/machine/AzAnimationControllerStateMachine.java new file mode 100644 index 000000000..6e40eb0c8 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/machine/AzAnimationControllerStateMachine.java @@ -0,0 +1,105 @@ +package mod.azure.azurelib.rewrite.animation.controller.state.machine; + +import mod.azure.azurelib.rewrite.animation.AzAnimationContext; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.state.AzAnimationState; +import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationPauseState; +import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationPlayState; +import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationStopState; +import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationTransitionState; +import mod.azure.azurelib.rewrite.util.state.StateMachine; +import mod.azure.azurelib.rewrite.util.state.StateMachineContext; + +/** + * A state machine for managing animation controller states, providing functionality to transition between play, pause, + * stop, and transition states. It is generic and supports handling context and states specific to animations. + * + * @param the type of the animation the state machine controls + */ +public class AzAnimationControllerStateMachine extends StateMachine, AzAnimationState> { + + private final StateHolder stateHolder; + + public AzAnimationControllerStateMachine( + StateHolder stateHolder, + AzAnimationController animationController, + AzAnimationContext animationContext + ) { + super(stateHolder.stopState()); + this.stateHolder = stateHolder; + getContext().stateMachine = this; + getContext().animationController = animationController; + getContext().animationContext = animationContext; + } + + @Override + public Context createContext() { + return new Context<>(); + } + + public void update() { + super.update(getContext()); + } + + public void pause() { + setState(stateHolder.pauseState); + } + + public void play() { + setState(stateHolder.playState); + } + + public void transition() { + setState(stateHolder.transitionState); + } + + public void stop() { + setState(stateHolder.stopState); + } + + public boolean isPlaying() { + return getState() == stateHolder.playState; + } + + public boolean isPaused() { + return getState() == stateHolder.pauseState; + } + + public boolean isStopped() { + return getState() == stateHolder.stopState; + } + + public boolean isTransitioning() { + return getState() == stateHolder.transitionState; + } + + public record StateHolder( + AzAnimationPlayState playState, + AzAnimationPauseState pauseState, + AzAnimationStopState stopState, + AzAnimationTransitionState transitionState + ) {} + + public static class Context implements StateMachineContext { + + private AzAnimationContext animationContext; + + private AzAnimationController animationController; + + private AzAnimationControllerStateMachine stateMachine; + + private Context() {} + + public AzAnimationContext animationContext() { + return animationContext; + } + + public AzAnimationController animationController() { + return animationController; + } + + public AzAnimationControllerStateMachine stateMachine() { + return stateMachine; + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java new file mode 100644 index 000000000..a5e97470b --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java @@ -0,0 +1,54 @@ +package mod.azure.azurelib.rewrite.animation.dispatch; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.util.StringRepresentable; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +/** + * This enum represents the dispatch side for animation commands, which can either be client-side or server-side. It is + * used as part of the AzureLib animation system for identifying where an animation command originates from or should be + * executed.
+ * Each enum constant has an associated unique identifier for easy lookup and transmission across the network. This + * mapping is also used within codecs for serialization and deserialization purposes. + */ +public enum AzDispatchSide implements StringRepresentable { + + CLIENT(0), + SERVER(1); + + private static final Map ID_TO_ENUM_MAP = new Int2ObjectArrayMap<>(); + + static { + // Populate the map for quick lookup + for (AzDispatchSide side : values()) { + ID_TO_ENUM_MAP.put(side.id, side); + } + } + + private final int id; + + AzDispatchSide(int id) { + this.id = id; + } + + public void encode(@NotNull FriendlyByteBuf buf, @NotNull AzDispatchSide val) { + buf.writeByte(val.id); + } + + public AzDispatchSide decode(@NotNull FriendlyByteBuf buf) { + int id = buf.readByte(); + AzDispatchSide side = ID_TO_ENUM_MAP.get(id); + if (side == null) { + throw new IllegalArgumentException("Invalid AzDispatchSide ID: " + id); + } + return side; + } + + @Override + public @NotNull String getSerializedName() { + return name(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java new file mode 100644 index 000000000..fd797d6bc --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java @@ -0,0 +1,175 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.network.packet.AzBlockEntityDispatchCommandPacket; +import mod.azure.azurelib.network.packet.AzEntityDispatchCommandPacket; +import mod.azure.azurelib.network.packet.AzItemStackDispatchCommandPacket; +import mod.azure.azurelib.platform.Services; +import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; +import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehavior; +import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviors; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Represents a command structure used to dispatch a sequence of actions in the animation system. This class primarily + * serves as a container for a list of {@link AzAction} instances that define specific operations or behaviors to be + * executed.
+ * The class provides support for building complex dispatch commands by leveraging the hierarchical builder system, + * enabling customization of animation-related functionality. + */ +public record AzCommand(List actions) { + + public void encode(FriendlyByteBuf buf, AzCommand command) { + // Write the size of the actions list + buf.writeInt(actions.size()); + + // Encode each AzAction in the list + for (AzAction action : actions) { + actions.encode(buf, action); + } + } + + public static AzCommand decode(FriendlyByteBuf buf) { + // Read the size of the actions list + int size = buf.readInt(); + + // Decode each AzAction and collect them into a list + for (int i = 0; i < size; i++) { + actions.add(AzAction.decode(buf)); + } + + // Return the new AzCommand with the decoded actions + return new AzCommand(actions); + } + + + public static AzRootCommandBuilder builder() { + return new AzRootCommandBuilder(); + } + + public static AzCommand compose(Collection commands) { + if (commands.isEmpty()) { + throw new IllegalArgumentException("Attempted to compose an empty collection of commands."); + } else if (commands.size() == 1) { + return commands.iterator().next(); + } + + return new AzCommand( + commands.stream() + .flatMap(command -> command.actions().stream()) + .toList() + ); + } + + public static AzCommand compose(AzCommand first, AzCommand second, AzCommand... others) { + var allCommands = new ArrayList(); + + allCommands.add(first); + allCommands.add(second); + Collections.addAll(allCommands, others); + + return compose(allCommands); + } + + public static AzCommand create(String controllerName, String animationName) { + return create(controllerName, animationName, AzPlayBehaviors.PLAY_ONCE); + } + + /** + * Creates a dispatch command to play a specified animation on a given controller. + * + * @param controllerName the name of the animation controller on which the animation should be played + * @param animationName the name of the animation to be played on the specified controller + * @param playBehavior the play behavior for the animation to use + * @return an instance of {@code AzCommand} representing the command to play the desired animation + */ + public static AzCommand create(String controllerName, String animationName, AzPlayBehavior playBehavior) { + return builder() + .playSequence( + controllerName, + sequenceBuilder -> sequenceBuilder.queue(animationName, props -> props.withPlayBehavior(playBehavior)) + ) + .build(); + } + + /** + * Sends animation commands for the specified entity based on the configured dispatch origin. The method determines + * whether the command should proceed, logs a warning if it cannot, and dispatches the animation commands either + * from the client or the server side. + * + * @param entity the target {@link Entity} for which the animation commands are dispatched. + */ + public void sendForEntity(Entity entity) { + if (entity.level().isClientSide()) { + dispatchFromClient(entity); + } else { + var entityId = entity.getId(); + var packet = new AzEntityDispatchCommandPacket(entityId, this); + Services.NETWORK.sendToTrackingEntityAndSelf(packet, entity); + } + } + + /** + * Sends animation commands for the specified block entity based on the configured dispatch origin. The method + * determines whether the command should proceed, logs a warning if it cannot, and dispatches the animation commands + * either from the client or the server side. + * + * @param entity the target {@link BlockEntity} for which the animation commands are dispatched. + */ + public void sendForBlockEntity(BlockEntity entity) { + if (entity.getLevel().isClientSide()) { + dispatchFromClient(entity); + } else { + var entityBlockPos = entity.getBlockPos(); + var packet = new AzBlockEntityDispatchCommandPacket(entityBlockPos, this); + Services.NETWORK.sendToEntitiesTrackingChunk(packet, (ServerLevel) entity.getLevel(), entityBlockPos); + } + } + + /** + * Sends animation commands for the specified item based on the configured dispatch origin. The method determines + * whether the command can proceed, assigns a unique identifier to the item if required, and dispatches the + * animation commands either from the client or the server side. + * + * @param entity the {@link Entity} associated with the {@link ItemStack}. + * @param itemStack the {@link ItemStack} on which the animation commands are dispatched. + */ + public void sendForItem(Entity entity, ItemStack itemStack) { + if (entity.level().isClientSide()) { + dispatchFromClient(entity); + } else { + var uuid = itemStack.getTag().getUUID("az_id"); + + if (uuid == null) { + AzureLib.LOGGER.warn( + "Could not find item stack UUID during dispatch. Did you forget to register an identity for the item? Item: {}, Item Stack: {}", + itemStack.getItem(), + itemStack + ); + return; + } + + var packet = new AzItemStackDispatchCommandPacket(uuid, this); + Services.NETWORK.sendToTrackingEntityAndSelf(packet, entity); + } + } + + private void dispatchFromClient(T animatable) { + var animator = AzAnimatorAccessor.getOrNull(animatable); + + if (animator != null) { + actions.forEach(action -> action.handle(AzDispatchSide.CLIENT, animator)); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommandBuilder.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommandBuilder.java new file mode 100644 index 000000000..e0aeb1a51 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommandBuilder.java @@ -0,0 +1,19 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AzCommandBuilder { + + protected final List actions; + + protected AzCommandBuilder() { + this.actions = new ArrayList<>(); + } + + public AzCommand build() { + return new AzCommand(actions); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzRootCommandBuilder.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzRootCommandBuilder.java new file mode 100644 index 000000000..c19be47a2 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzRootCommandBuilder.java @@ -0,0 +1,53 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.*; +import mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequenceBuilder; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; + +import java.util.function.UnaryOperator; + +public class AzRootCommandBuilder extends AzCommandBuilder { + + public AzRootCommandBuilder append(AzCommand command) { + actions.addAll(command.actions()); + return this; + } + + public AzRootCommandBuilder cancelAll() { + actions.add(new AzRootCancelAllAction()); + return this; + } + + public AzRootCommandBuilder setEasingType(AzEasingType easingType) { + actions.add(new AzRootSetEasingTypeAction(easingType)); + return this; + } + + public AzRootCommandBuilder setSpeed(float speed) { + actions.add(new AzRootSetAnimationSpeedAction(speed)); + return this; + } + + public AzRootCommandBuilder setTransitionSpeed(float transitionSpeed) { + actions.add(new AzRootSetTransitionSpeedAction(transitionSpeed)); + return this; + } + + public AzRootCommandBuilder cancel(String controllerName) { + actions.add(new AzRootCancelAction(controllerName)); + return this; + } + + public AzRootCommandBuilder play(String controllerName, String animationName) { + return playSequence(controllerName, builder -> builder.queue(animationName)); + } + + public AzRootCommandBuilder playSequence( + String controllerName, + UnaryOperator builderUnaryOperator + ) { + var sequence = builderUnaryOperator.apply(new AzAnimationSequenceBuilder()).build(); + actions.add(new AzRootPlayAnimationSequenceAction(controllerName, sequence)); + return this; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java new file mode 100644 index 000000000..0de6ad02e --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java @@ -0,0 +1,25 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.action; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootCancelAction; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +/** + * The AzAction interface serves as a base contract for defining actions that can be dispatched within the animation + * system. It provides methods for handling an action and retrieving its unique resource location identifier. + * Implementations of this interface encapsulate specific animation-related behaviors, allowing for the modification or + * control of animation states or properties within an {@link AzAnimator}. + */ +public interface AzAction { + + void handle(AzDispatchSide originSide, AzAnimator animator); + + ResourceLocation getResourceLocation(); + + void encode(@NotNull FriendlyByteBuf buf, @NotNull T action); + + T decode(@NotNull FriendlyByteBuf buf); +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java new file mode 100644 index 000000000..deb77b4be --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java @@ -0,0 +1,70 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.action.codec; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.registry.AzActionRegistry; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import org.jetbrains.annotations.NotNull; + +/** + * The AzActionCodec class serves as an implementation of the {@link StreamCodec} interface specifically designed for + * encoding and decoding {@link AzAction} objects. This codec encodes and decodes AzAction instances using their + * associated resource locations and registered codecs within the {@link AzActionRegistry}.
+ * This class provides the necessary functionality to serialize an AzAction to a {@link FriendlyByteBuf} and deserialize + * it back, ensuring proper handling of resource location and associated data. It relies on the AzActionRegistry to + * dynamically retrieve the appropriate codec and handle the serialization or deserialization process.
+ * Use this implementation in scenarios where AzAction objects need to be serialized or deserialized for efficient data + * transmission or storage. + */ +public class AzActionCodec { + + public static AzAction decode(FriendlyByteBuf byteBuf) { + // Read the ID of the action + var id = byteBuf.readShort(); + + // Retrieve the corresponding codec from the registry + var codec = AzActionRegistry + .getActionClassOrNull(id); + + // Throw an error if the codec is not found + if (codec == null) { + throw new NullPointerException( + "Could not find action codec for a given action id while decoding data. ID: " + id + ); + } + + // Use the codec to decode the action + return codec.decode(byteBuf); + } + + public static void encode(FriendlyByteBuf byteBuf, AzAction action) { + // Get the resource location of the action + var resourceLocation = action.getResourceLocation(); + + // Retrieve the ID and the corresponding codec for the action + var id = AzActionRegistry.getIdOrNull(resourceLocation); + var codec = AzActionRegistry + .getCodecOrNull(resourceLocation); + + // Throw an error if either the ID or the codec is not found + if (id == null) { + throw new NullPointerException( + "Could not find action id for a given resource location while encoding data. Resource Location: " + + resourceLocation + ); + } + + byteBuf.writeShort(id); + + if (codec == null) { + throw new NullPointerException( + "Could not find action codec for a given resource location while encoding data. Resource Location: " + + resourceLocation + ", ID: " + id + ); + } + + // Use the codec to encode the action + codec.encode(byteBuf, action); + } + +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java new file mode 100644 index 000000000..70902413f --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java @@ -0,0 +1,56 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +/** + * Represents an action to cancel the current animation, associated with a specific animation controller by its name. + * This action is part of the AzureLib animation framework and is used to stop an ongoing animation + * by clearing the current animation state of the specified controller. + *
+ * This class implements the {@link AzAction} interface, allowing it to be dispatched + * to modify animation states within an {@link AzAnimator}. + *
+ * The action can be serialized and deserialized for network communication or storage purposes. + */ +public record AzRootCancelAction( + String controllerName +) implements AzAction { + + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/cancel"); + + @Override + public void handle(AzDispatchSide originSide, AzAnimator animator) { + var controller = animator.getAnimationControllerContainer().getOrNull(controllerName); + + if (controller != null) { + controller.setCurrentAnimation(null); + } + } + + @Override + public ResourceLocation getResourceLocation() { + return RESOURCE_LOCATION; + } + + @Override + public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { + var cancelAction = (AzRootCancelAction) action; + buf.writeUtf(cancelAction.controllerName); + } + + @Override + public T decode(@NotNull FriendlyByteBuf buf) { + String controllerName = buf.readUtf(); + try { + return actionClass.getDeclaredConstructor(String.class).newInstance(controllerName); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Failed to decode action for class: " + actionClass.getName(), e); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java new file mode 100644 index 000000000..31af91818 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java @@ -0,0 +1,52 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + + +public class AzRootCancelAllAction implements AzAction { + + public static final AzRootCancelAllAction INSTANCE = new AzRootCancelAllAction(); + + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/cancel_all"); + + public static AzRootCancelAllAction getInstance() { + return INSTANCE; + } + + @Override + public void handle(AzDispatchSide originSide, AzAnimator animator) { + var controllerContainer = animator.getAnimationControllerContainer(); + var controllers = controllerContainer.getAll(); + + controllers.forEach(controller -> controller.setCurrentAnimation(null)); + } + + @Override + public ResourceLocation getResourceLocation() { + return RESOURCE_LOCATION; + } + + @Override + public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { + + } + + @Override + public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { + if (!AzRootCancelAllAction.class.equals(actionClass)) { + throw new IllegalArgumentException("Unsupported action class: " + actionClass.getName()); + } + + // Safe cast because we ensured the class type is AzRootCancelAllAction + @SuppressWarnings("unchecked") + T action = (T) INSTANCE; + + return action; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java new file mode 100644 index 000000000..c876021bd --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java @@ -0,0 +1,52 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; +import mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequence; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +public record AzRootPlayAnimationSequenceAction( + String controllerName, + AzAnimationSequence sequence +) implements AzAction { + + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/play_animation_sequence"); + + @Override + public void handle(AzDispatchSide originSide, AzAnimator animator) { + var controller = animator.getAnimationControllerContainer().getOrNull(controllerName); + + if (controller != null) { + controller.run(originSide, sequence); + } + } + + @Override + public ResourceLocation getResourceLocation() { + return RESOURCE_LOCATION; + } + + @Override + public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { + var rootAction = (AzRootPlayAnimationSequenceAction) action; + buf.writeUtf(rootAction.controllerName()); + AzAnimationSequence.encode(buf, rootAction.sequence()); + } + + @Override + public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { + if (!AzRootPlayAnimationSequenceAction.class.equals(actionClass)) { + throw new IllegalArgumentException("Unsupported action class: " + actionClass.getName()); + } + + var controllerName = buf.readUtf(); + + @SuppressWarnings("unchecked") + T action = (T) new AzRootPlayAnimationSequenceAction(controllerName, sequence); + return action; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java new file mode 100644 index 000000000..445a9ff05 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java @@ -0,0 +1,47 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +public record AzRootSetAnimationSpeedAction( + double animationSpeed +) implements AzAction { + + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/set_animation_speed"); + + @Override + public void handle(AzDispatchSide originSide, AzAnimator animator) { + animator.getAnimationControllerContainer() + .getAll() + .forEach( + controller -> controller.setAnimationProperties( + controller.animationProperties().withAnimationSpeed(animationSpeed) + ) + ); + } + + @Override + public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { + buf.writeDouble(animationSpeed); + } + + @Override + public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { + Double animationSpeed = buf.readDouble(); + try { + return actionClass.getDeclaredConstructor(Double.class).newInstance(animationSpeed); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Failed to decode action for class: " + actionClass.getName(), e); + } + } + + @Override + public ResourceLocation getResourceLocation() { + return RESOURCE_LOCATION; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java new file mode 100644 index 000000000..2b82c1c3b --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java @@ -0,0 +1,51 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +public record AzRootSetEasingTypeAction( + AzEasingType easingType +) implements AzAction { + + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/set_easing_type"); + + @Override + public void handle(AzDispatchSide originSide, AzAnimator animator) { + animator.getAnimationControllerContainer() + .getAll() + .forEach( + controller -> controller.setAnimationProperties( + controller.animationProperties().withEasingType(easingType) + ) + ); + } + + @Override + public ResourceLocation getResourceLocation() { + return RESOURCE_LOCATION; + } + + @Override + public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { + var easingAction = (AzRootSetEasingTypeAction) action; + AzEasingType.encode(buf, easingAction.easingType()); + } + + @Override + public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { + if (!AzRootSetEasingTypeAction.class.equals(actionClass)) { + throw new IllegalArgumentException("Unsupported action class: " + actionClass.getName()); + } + + @SuppressWarnings("unchecked") + T action = (T) new AzRootSetEasingTypeAction(easingType); + return action; + + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java new file mode 100644 index 000000000..073a99618 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java @@ -0,0 +1,52 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +public record AzRootSetTransitionSpeedAction( + float transitionSpeed +) implements AzAction { + + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/set_transition_speed"); + + @Override + public void handle(AzDispatchSide originSide, AzAnimator animator) { + animator.getAnimationControllerContainer() + .getAll() + .forEach( + controller -> controller.setAnimationProperties( + controller.animationProperties().withTransitionLength(transitionSpeed) + ) + ); + } + + @Override + public ResourceLocation getResourceLocation() { + return RESOURCE_LOCATION; + } + + @Override + public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { + var speedAction = (AzRootSetTransitionSpeedAction) action; + buf.writeFloat(speedAction.transitionSpeed()); + } + + @Override + public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { + if (!AzRootSetTransitionSpeedAction.class.equals(actionClass)) { + throw new IllegalArgumentException("Unsupported action class: " + actionClass.getName()); + } + + var transitionSpeed = buf.readFloat(); + + @SuppressWarnings("unchecked") + T action = (T) new AzRootSetTransitionSpeedAction(transitionSpeed); + return action; + + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java new file mode 100644 index 000000000..030e9843a --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java @@ -0,0 +1,111 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.action.registry; + +import it.unimi.dsi.fastutil.objects.Object2ShortArrayMap; +import mod.azure.azurelib.AzureLibException; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.*; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * The AzActionRegistry class is responsible for managing the registration and resolution of actions + * (implementations of {@link AzAction}) within an animation system. It serves as a centralized registry + * where each action is associated with a unique {@link ResourceLocation} and an internally generated + * identifier for efficient lookup. + */ +public class AzActionRegistry { + + private static final Map RESOURCE_LOCATION_TO_ID = new Object2ShortArrayMap<>(); + + private static final Map> ACTION_CLASS_BY_ID = + new HashMap<>(); + + private static short NEXT_FREE_ID = 0; + + static { + // Root actions + register(AzRootCancelAction.RESOURCE_LOCATION, AzRootCancelAction.class); + register(AzRootCancelAllAction.RESOURCE_LOCATION, AzRootCancelAllAction.class); + register(AzRootPlayAnimationSequenceAction.RESOURCE_LOCATION, AzRootPlayAnimationSequenceAction.class); + register(AzRootSetAnimationSpeedAction.RESOURCE_LOCATION, AzRootSetAnimationSpeedAction.class); + register(AzRootSetEasingTypeAction.RESOURCE_LOCATION, AzRootSetEasingTypeAction.class); + register(AzRootSetTransitionSpeedAction.RESOURCE_LOCATION, AzRootSetTransitionSpeedAction.class); + + // Controller actions + // TODO: + + // Animation actions + // TODO: + } + + public static @Nullable Class getActionClassOrNull(ResourceLocation resourceLocation) { + var id = RESOURCE_LOCATION_TO_ID.get(resourceLocation); + return id == null ? null : ACTION_CLASS_BY_ID.get(id); + } + + public static @Nullable Class getActionClassOrNull(short id) { + return ACTION_CLASS_BY_ID.get(id); + } + + public static @Nullable Short getIdOrNull(ResourceLocation resourceLocation) { + return RESOURCE_LOCATION_TO_ID.get(resourceLocation); + } + + private static
void register(ResourceLocation resourceLocation, Class clazz) { + Short id = RESOURCE_LOCATION_TO_ID.computeIfAbsent(resourceLocation, ($) -> NEXT_FREE_ID++); + ACTION_CLASS_BY_ID.put(id, clazz); + } + + public static @Nullable AzAction decode(FriendlyByteBuf buf) { + var id = buf.readShort(); + var actionClass = getActionClassOrNull(id); + + if (actionClass == null) { + throw new NullPointerException( + "Could not find action class for a given action id while decoding data. ID: " + id + ); + } + + try { + // Use the static decode method in the corresponding action class + return (AzAction) actionClass.getDeclaredMethod("decode", FriendlyByteBuf.class).invoke(null, buf); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to decode action for ID: " + id, e); + } + } + + public static void encode(FriendlyByteBuf buf, AzAction action) { + var resourceLocation = action.getResourceLocation(); + var id = getIdOrNull(resourceLocation); + var actionClass = getActionClassOrNull(resourceLocation); + + if (id == null) { + throw new NullPointerException( + "Could not find action id for a given resource location while encoding data. Resource Location: " + + resourceLocation + ); + } + + if (actionClass == null) { + throw new NullPointerException( + "Could not find action class for a given resource location while encoding data. Resource Location: " + + resourceLocation + ", ID: " + id + ); + } + + buf.writeShort(id); + + try { + // Use the static encode method in the corresponding action class + actionClass.getDeclaredMethod("encode", FriendlyByteBuf.class, AzAction.class) + .invoke(null, buf, action); + } catch (ReflectiveOperationException e) { + throw new AzureLibException("Failed to encode action for Resource Location: " + resourceLocation, e); + } + } + +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java new file mode 100644 index 000000000..3163caa40 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java @@ -0,0 +1,24 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.sequence; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.stage.AzAnimationStage; +import net.minecraft.network.FriendlyByteBuf; + +import java.util.List; + +public record AzAnimationSequence( + List stages +) { + + public static void encode(FriendlyByteBuf buf, AzAnimationSequence sequence) { + List stages = sequence.stages(); + buf.writeInt(stages.size()); + for (AzAnimationStage stage : stages) { + stage.encode(buf, stage); + } + } + + public AzAnimationSequence decode(FriendlyByteBuf buf) { + return new AzAnimationSequence(stages); + } + +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequenceBuilder.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequenceBuilder.java new file mode 100644 index 000000000..da07e3901 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequenceBuilder.java @@ -0,0 +1,35 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.sequence; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.stage.AzAnimationStage; +import mod.azure.azurelib.rewrite.animation.property.AzAnimationStageProperties; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.UnaryOperator; + +public class AzAnimationSequenceBuilder { + + private final List stages; + + public AzAnimationSequenceBuilder() { + this.stages = new ArrayList<>(); + } + + public AzAnimationSequenceBuilder queue(String animationName) { + stages.add(new AzAnimationStage(animationName, AzAnimationStageProperties.EMPTY)); + return this; + } + + public AzAnimationSequenceBuilder queue( + String animationName, + UnaryOperator builderUnaryOperator + ) { + var properties = builderUnaryOperator.apply(AzAnimationStageProperties.EMPTY); + stages.add(new AzAnimationStage(animationName, properties)); + return this; + } + + public AzAnimationSequence build() { + return new AzAnimationSequence(stages); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java new file mode 100644 index 000000000..c22c6a24e --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java @@ -0,0 +1,24 @@ +package mod.azure.azurelib.rewrite.animation.dispatch.command.stage; + +import mod.azure.azurelib.rewrite.animation.property.AzAnimationStageProperties; +import net.minecraft.network.FriendlyByteBuf; +import org.jetbrains.annotations.NotNull; + +public record AzAnimationStage( + String name, + AzAnimationStageProperties properties +) { + + public void encode(@NotNull FriendlyByteBuf buf, @NotNull AzAnimationStage stage) { + buf.writeUtf(stage.name()); + AzAnimationStageProperties.CODEC.encode(buf, stage.properties()); + } + + public AzAnimationStage decode(@NotNull FriendlyByteBuf buf) { + var name = buf.readUtf(); + var properties = AzAnimationStageProperties.CODEC.decode(buf); + + return new AzAnimationStage(name, properties); + } + +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java new file mode 100644 index 000000000..53520c351 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java @@ -0,0 +1,47 @@ +package mod.azure.azurelib.rewrite.animation.easing; + +import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; +import mod.azure.azurelib.core.utils.Interpolations; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzAnimationPoint; +import net.minecraft.network.FriendlyByteBuf; + +import java.util.Objects; + +public interface AzEasingType { + + String name(); + + Double2DoubleFunction buildTransformer(Double value); + + static void encode(FriendlyByteBuf buf, AzEasingType value) { + buf.writeUtf(value.name()); + } + + static AzEasingType decode(FriendlyByteBuf buf) { + return Objects.requireNonNull( + AzEasingTypeRegistry.getOrNull(buf.readUtf()), + "Invalid or unknown AzEasingType received" + ); + } + + + default double apply(AzAnimationPoint animationPoint) { + Double easingVariable = null; + + if (animationPoint.keyframe() != null && animationPoint.keyframe().easingArgs().size() > 0) + easingVariable = animationPoint.keyframe().easingArgs().get(0).get(); + + return apply(animationPoint, easingVariable, animationPoint.currentTick() / animationPoint.transitionLength()); + } + + default double apply(AzAnimationPoint animationPoint, Double easingValue, double lerpValue) { + if (animationPoint.currentTick() >= animationPoint.transitionLength()) + return (float) animationPoint.animationEndValue(); + + return Interpolations.lerp( + animationPoint.animationStartValue(), + animationPoint.animationEndValue(), + buildTransformer(easingValue).apply(lerpValue) + ); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeLoader.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeLoader.java new file mode 100644 index 000000000..65963f051 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeLoader.java @@ -0,0 +1,33 @@ +package mod.azure.azurelib.rewrite.animation.easing; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; + +import java.util.Locale; + +public class AzEasingTypeLoader { + + /** + * Retrieve an {@code EasingType} instance based on a {@link JsonElement}. Returns one of the default + * {@code EasingTypes} if the name matches, or any other registered {@code EasingType} with a matching name. + * + * @param json The {@code easing} {@link JsonElement} to attempt to parse. + * @return A usable {@code EasingType} instance + */ + public static AzEasingType fromJson(JsonElement json) { + if (!(json instanceof JsonPrimitive primitive) || !primitive.isString()) + return AzEasingTypes.LINEAR; + + return fromString(primitive.getAsString().toLowerCase(Locale.ROOT)); + } + + /** + * Get an existing {@code EasingType} from a given string, matching the string to its name. + * + * @param name The name of the easing function + * @return The relevant {@code EasingType}, or {@link AzEasingTypes#LINEAR} if none match + */ + public static AzEasingType fromString(String name) { + return AzEasingTypeRegistry.getOrDefault(name, AzEasingTypes.LINEAR); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeRegistry.java new file mode 100644 index 000000000..ff86f4ccd --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeRegistry.java @@ -0,0 +1,56 @@ +package mod.azure.azurelib.rewrite.animation.easing; + +import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +public class AzEasingTypeRegistry { + + private static final Map EASING_TYPES = new HashMap<>(); + + /** + * Register an {@code EasingType} with AzureLib for handling animation transitions and value curves.
+ * MUST be called during mod construct
+ * It is recommended you don't call this directly, and instead call it via {@code AzureLibUtil#addCustomEasingType} + * + * @param name The name of the easing type + * @param transformer The {@code Double2DoubleFunction} to associate with the given name + * @return The {@code EasingType} you registered + */ + public static AzEasingType register(String name, Function transformer) { + return EASING_TYPES.computeIfAbsent(name, ($) -> new AzEasingType() { + + @Override + public String name() { + return name; + } + + @Override + public Double2DoubleFunction buildTransformer(Double value) { + return transformer.apply(value); + } + }); + } + + public static AzEasingType register(String name, AzEasingType easingType) { + return register(name, easingType::buildTransformer); + } + + public static AzEasingType getOrDefault(String name, @NotNull AzEasingType defaultValue) { + return EASING_TYPES.getOrDefault(name, defaultValue); + } + + public static @Nullable AzEasingType getOrNull(String name) { + return EASING_TYPES.get(name); + } + + public static Collection getValues() { + return Collections.unmodifiableCollection(EASING_TYPES.values()); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypes.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypes.java new file mode 100644 index 000000000..2854d0eea --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypes.java @@ -0,0 +1,180 @@ +package mod.azure.azurelib.rewrite.animation.easing; + +public class AzEasingTypes { + + public static final AzEasingType NONE = AzEasingTypeRegistry.register( + "none", + value -> AzEasingUtil.easeIn(AzEasingUtil::linear) + ); + + public static final AzEasingType LINEAR = AzEasingTypeRegistry.register("linear", NONE); + + public static final AzEasingType STEP = AzEasingTypeRegistry.register( + "step", + value -> AzEasingUtil.easeIn(AzEasingUtil.step(value)) + ); + + public static final AzEasingType EASE_IN_SINE = AzEasingTypeRegistry.register( + "easeinsine", + value -> AzEasingUtil.easeIn(AzEasingUtil::sine) + ); + + public static final AzEasingType EASE_OUT_SINE = AzEasingTypeRegistry.register( + "easeoutsine", + value -> AzEasingUtil.easeOut(AzEasingUtil::sine) + ); + + public static final AzEasingType EASE_IN_OUT_SINE = AzEasingTypeRegistry.register( + "easeinoutsine", + value -> AzEasingUtil.easeInOut(AzEasingUtil::sine) + ); + + public static final AzEasingType EASE_IN_QUAD = AzEasingTypeRegistry.register( + "easeinquad", + value -> AzEasingUtil.easeIn(AzEasingUtil::quadratic) + ); + + public static final AzEasingType EASE_OUT_QUAD = AzEasingTypeRegistry.register( + "easeoutquad", + value -> AzEasingUtil.easeOut(AzEasingUtil::quadratic) + ); + + public static final AzEasingType EASE_IN_OUT_QUAD = AzEasingTypeRegistry.register( + "easeinoutquad", + value -> AzEasingUtil.easeInOut(AzEasingUtil::quadratic) + ); + + public static final AzEasingType EASE_IN_CUBIC = AzEasingTypeRegistry.register( + "easeincubic", + value -> AzEasingUtil.easeIn(AzEasingUtil::cubic) + ); + + public static final AzEasingType EASE_OUT_CUBIC = AzEasingTypeRegistry.register( + "easeoutcubic", + value -> AzEasingUtil.easeOut(AzEasingUtil::cubic) + ); + + public static final AzEasingType EASE_IN_OUT_CUBIC = AzEasingTypeRegistry.register( + "easeinoutcubic", + value -> AzEasingUtil.easeInOut(AzEasingUtil::cubic) + ); + + public static final AzEasingType EASE_IN_QUART = AzEasingTypeRegistry.register( + "easeinquart", + value -> AzEasingUtil.easeIn(AzEasingUtil.pow(4)) + ); + + public static final AzEasingType EASE_OUT_QUART = AzEasingTypeRegistry.register( + "easeoutquart", + value -> AzEasingUtil.easeOut(AzEasingUtil.pow(4)) + ); + + public static final AzEasingType EASE_IN_OUT_QUART = AzEasingTypeRegistry.register( + "easeinoutquart", + value -> AzEasingUtil.easeInOut(AzEasingUtil.pow(4)) + ); + + public static final AzEasingType EASE_IN_QUINT = AzEasingTypeRegistry.register( + "easeinquint", + value -> AzEasingUtil.easeIn(AzEasingUtil.pow(4)) + ); + + public static final AzEasingType EASE_OUT_QUINT = AzEasingTypeRegistry.register( + "easeoutquint", + value -> AzEasingUtil.easeOut(AzEasingUtil.pow(5)) + ); + + public static final AzEasingType EASE_IN_OUT_QUINT = AzEasingTypeRegistry.register( + "easeinoutquint", + value -> AzEasingUtil.easeInOut(AzEasingUtil.pow(5)) + ); + + public static final AzEasingType EASE_IN_EXPO = AzEasingTypeRegistry.register( + "easeinexpo", + value -> AzEasingUtil.easeIn(AzEasingUtil::exp) + ); + + public static final AzEasingType EASE_OUT_EXPO = AzEasingTypeRegistry.register( + "easeoutexpo", + value -> AzEasingUtil.easeOut(AzEasingUtil::exp) + ); + + public static final AzEasingType EASE_IN_OUT_EXPO = AzEasingTypeRegistry.register( + "easeinoutexpo", + value -> AzEasingUtil.easeInOut(AzEasingUtil::exp) + ); + + public static final AzEasingType EASE_IN_CIRC = AzEasingTypeRegistry.register( + "easeincirc", + value -> AzEasingUtil.easeIn(AzEasingUtil::circle) + ); + + public static final AzEasingType EASE_OUT_CIRC = AzEasingTypeRegistry.register( + "easeoutcirc", + value -> AzEasingUtil.easeOut(AzEasingUtil::circle) + ); + + public static final AzEasingType EASE_IN_OUT_CIRC = AzEasingTypeRegistry.register( + "easeinoutcirc", + value -> AzEasingUtil.easeInOut(AzEasingUtil::circle) + ); + + public static final AzEasingType EASE_IN_BACK = AzEasingTypeRegistry.register( + "easeinback", + value -> AzEasingUtil.easeIn(AzEasingUtil.back(value)) + ); + + public static final AzEasingType EASE_OUT_BACK = AzEasingTypeRegistry.register( + "easeoutback", + value -> AzEasingUtil.easeOut(AzEasingUtil.back(value)) + ); + + public static final AzEasingType EASE_IN_OUT_BACK = AzEasingTypeRegistry.register( + "easeinoutback", + value -> AzEasingUtil.easeInOut(AzEasingUtil.back(value)) + ); + + public static final AzEasingType EASE_IN_ELASTIC = AzEasingTypeRegistry.register( + "easeinelastic", + value -> AzEasingUtil.easeIn(AzEasingUtil.elastic(value)) + ); + + public static final AzEasingType EASE_OUT_ELASTIC = AzEasingTypeRegistry.register( + "easeoutelastic", + value -> AzEasingUtil.easeOut(AzEasingUtil.elastic(value)) + ); + + public static final AzEasingType EASE_IN_OUT_ELASTIC = AzEasingTypeRegistry.register( + "easeinoutelastic", + value -> AzEasingUtil.easeInOut(AzEasingUtil.elastic(value)) + ); + + public static final AzEasingType EASE_IN_BOUNCE = AzEasingTypeRegistry.register( + "easeinbounce", + value -> AzEasingUtil.easeIn(AzEasingUtil.bounce(value)) + ); + + public static final AzEasingType EASE_OUT_BOUNCE = AzEasingTypeRegistry.register( + "easeoutbounce", + value -> AzEasingUtil.easeOut(AzEasingUtil.bounce(value)) + ); + + public static final AzEasingType EASE_IN_OUT_BOUNCE = AzEasingTypeRegistry.register( + "easeinoutbounce", + value -> AzEasingUtil.easeInOut(AzEasingUtil.bounce(value)) + ); + + public static final AzEasingType CATMULLROM = AzEasingTypeRegistry.register( + "catmullrom", + value -> AzEasingUtil.easeInOut(AzEasingUtil::catmullRom) + ); + + public static AzEasingType random() { + var collection = AzEasingTypeRegistry.getValues(); + + return collection.stream() + .skip((int) (collection.size() * Math.random())) + .findFirst() + .orElse(null); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingUtil.java new file mode 100644 index 000000000..bd29375ab --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingUtil.java @@ -0,0 +1,249 @@ +package mod.azure.azurelib.rewrite.animation.easing; + +import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzAnimationPoint; + +public class AzEasingUtil { + + /** + * Returns an easing function running linearly. Functionally equivalent to no easing + */ + public static Double2DoubleFunction linear(Double2DoubleFunction function) { + return function; + } + + /** + * Performs a Catmull-Rom interpolation, used to get smooth interpolated motion between keyframes.
+ *
CatmullRom#position + */ + public static double catmullRom(double n) { + return (0.5f * (2.0f * (n + 1) + ((n + 2) - n) * 1 + + (2.0f * n - 5.0f * (n + 1) + 4.0f * (n + 2) - (n + 3)) * 1 + + (3.0f * (n + 1) - n - 3.0f * (n + 2) + (n + 3)) * 1)); + } + + /** + * Returns an easing function running forward in time + */ + public static Double2DoubleFunction easeIn(Double2DoubleFunction function) { + return function; + } + + // ---> Easing Transition Type Functions <--- // + + /** + * Returns an easing function running backwards in time + */ + public static Double2DoubleFunction easeOut(Double2DoubleFunction function) { + return time -> 1 - function.apply(1 - time); + } + + /** + * Returns an easing function that runs equally both forwards and backwards in time based on the halfway point, + * generating a symmetrical curve.
+ */ + public static Double2DoubleFunction easeInOut(Double2DoubleFunction function) { + return time -> { + if (time < 0.5d) + return function.apply(time * 2d) / 2d; + + return 1 - function.apply((1 - time) * 2d) / 2d; + }; + } + + /** + * Returns a stepping function that returns 1 for any input value greater than 0, or otherwise returning 0 + */ + public static Double2DoubleFunction stepPositive(Double2DoubleFunction function) { + return n -> n > 0 ? 1 : 0; + } + + /** + * Returns a stepping function that returns 1 for any input value greater than or equal to 0, or otherwise returning + * 0 + */ + public static Double2DoubleFunction stepNonNegative(Double2DoubleFunction function) { + return n -> n >= 0 ? 1 : 0; + } + + /** + * A linear function, equivalent to a null-operation.
+ * {@code f(n) = n} + */ + public static double linear(double n) { + return n; + } + + // ---> Stepping Functions <--- // + + /** + * A quadratic function, equivalent to the square (n^2) of elapsed time.
+ * {@code f(n) = n^2}
+ * Easings.net#easeInQuad + */ + public static double quadratic(double n) { + return n * n; + } + + /** + * A cubic function, equivalent to cube (n^3) of elapsed time.
+ * {@code f(n) = n^3}
+ * Easings.net#easeInCubic + */ + public static double cubic(double n) { + return n * n * n; + } + + // ---> Mathematical Functions <--- // + + /** + * A sinusoidal function, equivalent to a sine curve output.
+ * {@code f(n) = 1 - cos(n * π / 2)}
+ * Easings.net#easeInSine + */ + public static double sine(double n) { + return 1 - Math.cos(n * Math.PI / 2f); + } + + /** + * A circular function, equivalent to a normally symmetrical curve.
+ * {@code f(n) = 1 - sqrt(1 - n^2)}
+ * Easings.net#easeInCirc + */ + public static double circle(double n) { + return 1 - Math.sqrt(1 - n * n); + } + + /** + * An exponential function, equivalent to an exponential curve.
+ * {@code f(n) = 2^(10 * (n - 1))}
+ * Easings.net#easeInExpo + */ + public static double exp(double n) { + return Math.pow(2, 10 * (n - 1)); + } + + /** + * An elastic function, equivalent to an oscillating curve.
+ * n defines the elasticity of the output.
+ * {@code f(t) = 1 - (cos(t * π) / 2))^3 * cos(t * n * π)}
+ * Easings.net#easeInElastic + */ + public static Double2DoubleFunction elastic(Double n) { + double n2 = n == null ? 1 : n; + + return t -> 1 - Math.pow(Math.cos(t * Math.PI / 2f), 3) * Math.cos(t * n2 * Math.PI); + } + + /** + * A bouncing function, equivalent to a bouncing ball curve.
+ * n defines the bounciness of the output.
+ * Thanks to Waterded#6455 for making the bounce adjustable, and GiantLuigi4#6616 for additional + * cleanup.
+ * Easings.net#easeInBounce + */ + public static Double2DoubleFunction bounce(Double n) { + final double n2 = n == null ? 0.5d : n; + + Double2DoubleFunction one = x -> 121f / 16f * x * x; + Double2DoubleFunction two = x -> 121f / 4f * n2 * Math.pow(x - 6f / 11f, 2) + 1 - n2; + Double2DoubleFunction three = x -> 121 * n2 * n2 * Math.pow(x - 9f / 11f, 2) + 1 - n2 * n2; + Double2DoubleFunction four = x -> 484 * n2 * n2 * n2 * Math.pow(x - 10.5f / 11f, 2) + 1 - n2 * n2 * n2; + + return t -> Math.min(Math.min(one.apply(t), two.apply(t)), Math.min(three.apply(t), four.apply(t))); + } + + /** + * A negative elastic function, equivalent to inverting briefly before increasing.
+ * f(t) = t^2 * ((n * 1.70158 + 1) * t - n * 1.70158)
+ * Easings.net#easeInBack + */ + public static Double2DoubleFunction back(Double n) { + final double n2 = n == null ? 1.70158d : n * 1.70158d; + + return t -> t * t * ((n2 + 1) * t - n2); + } + + // ---> Easing Curve Functions <--- // + + /** + * An exponential function, equivalent to an exponential curve to the {@code n} root.
+ * f(t) = t^n + * + * @param n The exponent + */ + public static Double2DoubleFunction pow(double n) { + return t -> Math.pow(t, n); + } + + /** + * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Boris Chumichev
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE.
+ *
+ * Returns a stepped value based on the nearest step to the input value.
+ * The size (grade) of the steps depends on the provided value of {@code n} + **/ + public static Double2DoubleFunction step(Double n) { + double n2 = n == null ? 2 : n; + + if (n2 < 2) + throw new IllegalArgumentException("Steps must be >= 2, got: " + n2); + + final int steps = (int) n2; + + return t -> { + double result = 0; + + if (t < 0) + return result; + + double stepLength = (1 / (double) steps); + + if (t > (result = (steps - 1) * stepLength)) + return result; + + int testIndex; + int leftBorderIndex = 0; + int rightBorderIndex = steps - 1; + + while (rightBorderIndex - leftBorderIndex != 1) { + testIndex = leftBorderIndex + (rightBorderIndex - leftBorderIndex) / 2; + + if (t >= testIndex * stepLength) { + leftBorderIndex = testIndex; + } else { + rightBorderIndex = testIndex; + } + } + + return leftBorderIndex * stepLength; + }; + } + + public static double lerpWithOverride(AzAnimationPoint animationPoint, AzEasingType override) { + var easingType = override; + + if (override == null) { + easingType = animationPoint.keyframe() == null + ? AzEasingTypes.LINEAR + : animationPoint.keyframe().easingType(); + } + + return easingType.apply(animationPoint); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzCustomInstructionKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzCustomInstructionKeyframeEvent.java new file mode 100644 index 000000000..2fc40463e --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzCustomInstructionKeyframeEvent.java @@ -0,0 +1,29 @@ +package mod.azure.azurelib.rewrite.animation.event; + +import mod.azure.azurelib.core.keyframe.event.data.CustomInstructionKeyframeData; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeCallbacks; + +/** + * The {@link AzKeyframeEvent} specific to the {@link AzKeyframeCallbacks#customKeyframeHandler()}.
+ * Called when a custom instruction keyframe is encountered + */ +public class AzCustomInstructionKeyframeEvent extends AzKeyframeEvent { + + public AzCustomInstructionKeyframeEvent( + T entity, + double animationTick, + AzAnimationController controller, + CustomInstructionKeyframeData customInstructionKeyframeData + ) { + super(entity, animationTick, controller, customInstructionKeyframeData); + } + + /** + * Get the {@link CustomInstructionKeyframeData} relevant to this event call + */ + @Override + public CustomInstructionKeyframeData getKeyframeData() { + return super.getKeyframeData(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzKeyframeEvent.java new file mode 100644 index 000000000..96c56a53c --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzKeyframeEvent.java @@ -0,0 +1,65 @@ +package mod.azure.azurelib.rewrite.animation.event; + +import mod.azure.azurelib.core.keyframe.Keyframe; +import mod.azure.azurelib.core.keyframe.event.data.KeyFrameData; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; + +/** + * The base class for {@link Keyframe} events.
+ * These will be passed to one of the controllers in {@link AzAnimationController} when encountered during animation. + * + * @see AzCustomInstructionKeyframeEvent + * @see AzParticleKeyframeEvent + * @see AzSoundKeyframeEvent + */ +public abstract class AzKeyframeEvent { + + private final T animatable; + + private final double animationTick; + + private final AzAnimationController controller; + + private final E eventKeyframe; + + protected AzKeyframeEvent( + T animatable, + double animationTick, + AzAnimationController controller, + E eventKeyframe + ) { + this.animatable = animatable; + this.animationTick = animationTick; + this.controller = controller; + this.eventKeyframe = eventKeyframe; + } + + /** + * Gets the amount of ticks that have passed in either the current transition or animation, depending on the + * controller's AnimationState. + */ + public double getAnimationTick() { + return animationTick; + } + + /** + * Gets the {@link T animatable} object being rendered + */ + public T getAnimatable() { + return animatable; + } + + /** + * Gets the {@link AzAnimationController} responsible for the currently playing animation + */ + public AzAnimationController getController() { + return controller; + } + + /** + * Returns the {@link KeyFrameData} relevant to the encountered {@link Keyframe} + */ + public E getKeyframeData() { + return this.eventKeyframe; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzParticleKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzParticleKeyframeEvent.java new file mode 100644 index 000000000..2c7ca81af --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzParticleKeyframeEvent.java @@ -0,0 +1,34 @@ +/** + * This class is a fork of the matching class found in the Geckolib repository. Original source: + * https://github.com/bernie-g/geckolib Copyright © 2024 Bernie-G. Licensed under the MIT License. + * https://github.com/bernie-g/geckolib/blob/main/LICENSE + */ +package mod.azure.azurelib.rewrite.animation.event; + +import mod.azure.azurelib.core.keyframe.event.data.ParticleKeyframeData; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeCallbacks; + +/** + * The {@link AzKeyframeEvent} specific to the {@link AzKeyframeCallbacks#particleKeyframeHandler()}.
+ * Called when a particle instruction keyframe is encountered + */ +public class AzParticleKeyframeEvent extends AzKeyframeEvent { + + public AzParticleKeyframeEvent( + T animatable, + double animationTick, + AzAnimationController controller, + ParticleKeyframeData particleKeyframeData + ) { + super(animatable, animationTick, controller, particleKeyframeData); + } + + /** + * Get the {@link ParticleKeyframeData} relevant to this event call + */ + @Override + public ParticleKeyframeData getKeyframeData() { + return super.getKeyframeData(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzSoundKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzSoundKeyframeEvent.java new file mode 100644 index 000000000..0c27f74ba --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzSoundKeyframeEvent.java @@ -0,0 +1,34 @@ +/** + * This class is a fork of the matching class found in the Geckolib repository. Original source: + * https://github.com/bernie-g/geckolib Copyright © 2024 Bernie-G. Licensed under the MIT License. + * https://github.com/bernie-g/geckolib/blob/main/LICENSE + */ +package mod.azure.azurelib.rewrite.animation.event; + +import mod.azure.azurelib.core.keyframe.event.data.SoundKeyframeData; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeCallbacks; + +/** + * The {@link AzKeyframeEvent} specific to the {@link AzKeyframeCallbacks#soundKeyframeHandler()}.
+ * Called when a sound instruction keyframe is encountered + */ +public class AzSoundKeyframeEvent extends AzKeyframeEvent { + + /** + * This stores all the fields that are needed in the AnimationTestEvent + * + * @param entity the entity + * @param animationTick The amount of ticks that have passed in either the current transition or animation, + * depending on the controller's AnimationState. + * @param controller the controller + */ + public AzSoundKeyframeEvent( + T entity, + double animationTick, + AzAnimationController controller, + SoundKeyframeData keyframeData + ) { + super(entity, animationTick, controller, keyframeData); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzBlockAnimator.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzBlockAnimator.java new file mode 100644 index 000000000..f8cc38a4c --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzBlockAnimator.java @@ -0,0 +1,19 @@ +package mod.azure.azurelib.rewrite.animation.impl; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; +import net.minecraft.world.level.block.entity.BlockEntity; + +/** + * The {@code AzBlockAnimator} class extends the functionality of the {@link AzAnimator} to provide animation support + * specifically for {@link BlockEntity} instances. This abstract class serves as a base for creating block entity + * animators with reusable configuration and animation controller registration mechanisms. + * + * @param The type of {@link BlockEntity} that this animator will manage animations for. + */ +public abstract class AzBlockAnimator extends AzAnimator { + + protected AzBlockAnimator(AzAnimatorConfig config) { + super(config); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzEntityAnimator.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzEntityAnimator.java new file mode 100644 index 000000000..ed2d2400e --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzEntityAnimator.java @@ -0,0 +1,69 @@ +package mod.azure.azurelib.rewrite.animation.impl; + +import mod.azure.azurelib.core.molang.MolangParser; +import mod.azure.azurelib.core.molang.MolangQueries; +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; + +/** + * The {@code AzEntityAnimator} class extends {@link AzAnimator} to provide specialized animation management for + * entities. This abstract class is designed to handle various animation-related requirements for entities in a game + * framework, including the application of MoLang queries specific to entity-related properties such as position, + * health, and motion state. + * + * @param The type of entity this animator is designed to manage. + */ +public abstract class AzEntityAnimator extends AzAnimator { + + protected AzEntityAnimator() { + super(); + } + + protected AzEntityAnimator(AzAnimatorConfig config) { + super(config); + } + + /** + * Applies MoLang queries specific to an entity in the animation system. These queries provide contextual + * information about the entity's state and environment, such as its position, health, movement, and interaction + * with the world. The method extends the baseline queries defined in the superclass with additional entity-specific + * properties, particularly for living entities. + * + * @param entity The entity for which the MoLang queries are being applied. + * @param animTime The current animation time, in seconds, used for time-dependent queries. + */ + @Override + protected void applyMolangQueries(T entity, double animTime) { + super.applyMolangQueries(entity, animTime); + + var parser = MolangParser.INSTANCE; + var minecraft = Minecraft.getInstance(); + + parser.setMemoizedValue( + MolangQueries.DISTANCE_FROM_CAMERA, + () -> minecraft.gameRenderer.getMainCamera().getPosition().distanceTo(entity.position()) + ); + parser.setMemoizedValue(MolangQueries.IS_ON_GROUND, () -> RenderUtils.booleanToFloat(entity.onGround())); + parser.setMemoizedValue(MolangQueries.IS_IN_WATER, () -> RenderUtils.booleanToFloat(entity.isInWater())); + parser.setMemoizedValue( + MolangQueries.IS_IN_WATER_OR_RAIN, + () -> RenderUtils.booleanToFloat(entity.isInWaterOrRain()) + ); + parser.setMemoizedValue(MolangQueries.IS_ON_FIRE, () -> RenderUtils.booleanToFloat(entity.isOnFire())); + + if (entity instanceof LivingEntity livingEntity) { + parser.setMemoizedValue(MolangQueries.HEALTH, livingEntity::getHealth); + parser.setMemoizedValue(MolangQueries.MAX_HEALTH, livingEntity::getMaxHealth); + parser.setMemoizedValue(MolangQueries.GROUND_SPEED, () -> { + var velocity = livingEntity.getDeltaMovement(); + return Mth.sqrt((float) ((velocity.x * velocity.x) + (velocity.z * velocity.z))); + }); + parser.setMemoizedValue(MolangQueries.YAW_SPEED, () -> livingEntity.getYRot() - livingEntity.yRotO); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzItemAnimator.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzItemAnimator.java new file mode 100644 index 000000000..9fc39744b --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzItemAnimator.java @@ -0,0 +1,25 @@ +package mod.azure.azurelib.rewrite.animation.impl; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; +import net.minecraft.world.item.ItemStack; + +/** + * The {@code AzItemAnimator} class is an abstract extension of the {@code AzAnimator} class, specifically designed to + * handle animations for {@link ItemStack} objects. It provides common functionality and structure for animating items + * within the framework.
+ *
+ * This class serves as a base for developing custom item animator implementations. Subclasses are required to implement + * methods for animation controller registration and for specifying the animation location for the corresponding + * {@code ItemStack}. + */ +public abstract class AzItemAnimator extends AzAnimator { + + protected AzItemAnimator() { + super(); + } + + protected AzItemAnimator(AzAnimatorConfig config) { + super(config); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzBakedAnimationsAdapter.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzBakedAnimationsAdapter.java new file mode 100644 index 000000000..b54d517f6 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzBakedAnimationsAdapter.java @@ -0,0 +1,364 @@ +package mod.azure.azurelib.rewrite.animation.parse; + +import com.google.gson.*; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.core.keyframe.BoneAnimation; +import mod.azure.azurelib.core.keyframe.KeyframeStack; +import mod.azure.azurelib.core.math.Constant; +import mod.azure.azurelib.core.math.IValue; +import mod.azure.azurelib.core.molang.MolangException; +import mod.azure.azurelib.core.molang.MolangParser; +import mod.azure.azurelib.core.molang.expressions.MolangValue; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzBoneAnimation; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframe; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeStack; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypeLoader; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypes; +import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimation; +import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimations; +import mod.azure.azurelib.rewrite.animation.primitive.AzKeyframes; +import mod.azure.azurelib.rewrite.animation.primitive.AzLoopType; +import mod.azure.azurelib.util.JsonUtil; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.GsonHelper; +import org.apache.commons.lang3.math.NumberUtils; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +/** + * {@link Gson} {@link JsonDeserializer} for {@link AzBakedAnimations}.
+ * Acts as the deserialization interface for {@code BakedAnimations} + */ +public class AzBakedAnimationsAdapter implements JsonDeserializer { + + /** + * Processes a given JSON element and transforms it into a list of pairs, where each pair consists of a string key + * and a corresponding JSON element. Depending on the type of the input element, it handles primitive values, + * arrays, and objects differently, ensuring a uniform output structure. For JSON primitives, a synthetic triplet + * array is generated. For JSON arrays, the array is paired with the key "0". For JSON objects, individual entries + * are processed recursively, with special handling for nested objects without a "vector" key. + * + * @param element The JSON element to be processed. It can be a {@link JsonPrimitive}, {@link JsonObject}, or + * {@link JsonArray}. If null, an empty list is returned. + * @return A list of {@link Pair} objects where each pair contains a string key and a corresponding + * {@link JsonElement}. This list represents the processed structure of the input JSON element. + * @throws JsonParseException If the provided JSON element is of an unsupported type or is invalid. + */ + private static List> getTripletObj(JsonElement element) { + if (element == null) + return List.of(); + + if (element instanceof JsonPrimitive primitive) { + JsonArray array = new JsonArray(3); + + array.add(primitive); + array.add(primitive); + array.add(primitive); + + element = array; + } + + if (element instanceof JsonArray array) + return ObjectArrayList.of(Pair.of("0", array)); + + if (element instanceof JsonObject obj) { + List> list = new ObjectArrayList<>(); + + for (Map.Entry entry : obj.entrySet()) { + if (entry.getValue() instanceof JsonObject entryObj && !entryObj.has("vector")) { + list.add(getTripletObjBedrock(entry.getKey(), entryObj)); + + continue; + } + + list.add(Pair.of(entry.getKey(), entry.getValue())); + } + + return list; + } + + throw new JsonParseException("Invalid object type provided to getTripletObj, got: " + element); + } + + /** + * Extracts and processes keyframe data from a given JSON object, returning a pair consisting of a timestamp and + * associated JSON element data. The method focuses on retrieving either the "pre" or "post" keyframe data from the + * input JSON object, applying specific handling for array or object-based representations. + * + * @param timestamp The string representation of the timestamp for the keyframe data. If the input value is not + * valid as a numeric string, it defaults to "0". + * @param keyframe A {@link JsonObject} containing the keyframe data. Expected keys include "pre" or "post" with + * their associated values either as JSON arrays or nested objects containing a "vector" element. + * @return A {@link Pair} where the first element is the processed timestamp as a string, and the second element is + * a {@link JsonArray} representing the keyframe values. + * @throws JsonParseException If the provided keyframe data is invalid or does not meet the expected structure, such + * as missing or incorrectly formatted "pre" or "post" keys. + */ + private static Pair getTripletObjBedrock(String timestamp, JsonObject keyframe) { + JsonArray keyframeValues = null; + + if (keyframe.has("pre")) { + JsonElement pre = keyframe.get("pre"); + keyframeValues = pre.isJsonArray() + ? pre.getAsJsonArray() + : GsonHelper.getAsJsonArray(pre.getAsJsonObject(), "vector"); + } else if (keyframe.has("post")) { + JsonElement post = keyframe.get("post"); + keyframeValues = post.isJsonArray() + ? post.getAsJsonArray() + : GsonHelper.getAsJsonArray(post.getAsJsonObject(), "vector"); + } + + if (keyframeValues != null) + return Pair.of(NumberUtils.isCreatable(timestamp) ? timestamp : "0", keyframeValues); + + throw new JsonParseException("Invalid keyframe data - expected array, found " + keyframe); + } + + /** + * Calculates the overall length of the animation timeline across all provided bone animations. The calculation + * considers the maximum keyframe time for rotation, position, and scale transformations for each bone and + * determines the longest timeline among them. + * + * @param boneAnimations An array of {@link BoneAnimation} instances representing the animations for individual + * bones. Each bone animation includes keyframe stacks for rotation, position, and scale + * transformations. + * @return The maximum length of the animation timeline. If no keyframes are present, it defaults to + * {@link Double#MAX_VALUE}. + */ + private static double calculateAnimationLength(AzBoneAnimation[] boneAnimations) { + double length = 0; + + for (var animation : boneAnimations) { + length = Math.max(length, animation.rotationKeyframes().getLastKeyframeTime()); + length = Math.max(length, animation.positionKeyframes().getLastKeyframeTime()); + length = Math.max(length, animation.scaleKeyframes().getLastKeyframeTime()); + } + + return length == 0 ? Double.MAX_VALUE : length; + } + + /** + * Deserializes JSON data into an instance of {@link AzBakedAnimations}. + * + * @param json The JSON element to deserialize, expected to contain a valid structure for animations and optional + * includes. + * @param type The type of object to deserialize to; this is typically {@link AzBakedAnimations}. + * @param context A context for handling nested deserialization, such as for custom types embedded within the JSON + * structure. + * @return A newly created {@link AzBakedAnimations} instance containing parsed animations and includes as specified + * in the provided JSON data. + * @throws JsonParseException If the JSON structure is invalid or an error occurs during deserialization. + */ + @Override + public AzBakedAnimations deserialize( + JsonElement json, + Type type, + JsonDeserializationContext context + ) throws JsonParseException { + JsonObject jsonObj = json.getAsJsonObject(); + + JsonObject animationJsonList = jsonObj.getAsJsonObject("animations"); + JsonArray includeListJSONObj = jsonObj.getAsJsonArray("includes"); + Map includes = null; + if (includeListJSONObj != null) { + includes = new Object2ObjectOpenHashMap<>(includeListJSONObj.size()); + for (JsonElement entry : includeListJSONObj.asList()) { + JsonObject obj = entry.getAsJsonObject(); + ResourceLocation fileId = new ResourceLocation(obj.get("file_id").getAsString()); + for (JsonElement animName : obj.getAsJsonArray("animations")) { + String ani = animName.getAsString(); + if (includes.containsKey(ani)) { + AzureLib.LOGGER.warn( + "Animation {} is already included! File already including: {} File trying to include from again: {}", + ani, + includes.get(ani), + fileId + ); + } else { + includes.put(ani, fileId); + } + } + } + } + + Map animations = new Object2ObjectOpenHashMap<>(animationJsonList.size()); + + for (Map.Entry entry : animationJsonList.entrySet()) { + try { + animations.put( + entry.getKey(), + bakeAnimation(entry.getKey(), entry.getValue().getAsJsonObject(), context) + ); + } catch (MolangException ex) { + AzureLib.LOGGER.error("Unable to parse animation: {}", entry.getKey()); + ex.printStackTrace(); + } + } + + return new AzBakedAnimations(animations, includes); + } + + /** + * Processes the provided JSON data to create an instance of {@link AzBakedAnimation}. This method interprets the + * animation JSON object, constructs the necessary data structures such as bone animations and keyframes, and + * applies logic to calculate the animation length if not explicitly defined. + * + * @param name The name of the animation being created. + * @param animationObj The JSON object containing the animation definition. This object may include details such as + * animation length, loop type, bones, and keyframe data. + * @param context The deserialization context used for nested data structures such as {@link AzKeyframes}. + * @return A constructed {@link AzBakedAnimation} instance containing the parsed animation details. + * @throws MolangException If an error occurs while processing expressions or any other aspect of the Molang + * language during animation creation. + */ + private AzBakedAnimation bakeAnimation( + String name, + JsonObject animationObj, + JsonDeserializationContext context + ) throws MolangException { + double length = animationObj.has("animation_length") + ? GsonHelper.getAsDouble(animationObj, "animation_length") * 20d + : -1; + AzLoopType loopType = AzLoopType.fromJson(animationObj.get("loop")); + AzBoneAnimation[] boneAnimations = bakeBoneAnimations( + GsonHelper.getAsJsonObject(animationObj, "bones", new JsonObject()) + ); + AzKeyframes keyframes = context.deserialize(animationObj, AzKeyframes.class); + + if (length == -1) + length = calculateAnimationLength(boneAnimations); + + return new AzBakedAnimation(name, length, loopType, boneAnimations, keyframes); + } + + /** + * Processes a JSON object representing bone animations and constructs an array of {@link BoneAnimation} instances. + * Each bone's animation includes keyframe stacks for position, rotation, and scale transformations. + * + * @param bonesObj The JSON object containing bone animation data, where each key is the bone name and the value is + * an object with keyframe data for scale, position, and rotation. + * @return An array of {@link BoneAnimation} instances representing the deserialized animations for each bone. + * @throws MolangException If an error occurs during the processing of keyframes or Molang expressions. + */ + private AzBoneAnimation[] bakeBoneAnimations(JsonObject bonesObj) throws MolangException { + AzBoneAnimation[] animations = new AzBoneAnimation[bonesObj.size()]; + int index = 0; + + for (Map.Entry entry : bonesObj.entrySet()) { + JsonObject entryObj = entry.getValue().getAsJsonObject(); + AzKeyframeStack> scaleFrames = buildKeyframeStack( + getTripletObj(entryObj.get("scale")), + false + ); + AzKeyframeStack> positionFrames = buildKeyframeStack( + getTripletObj(entryObj.get("position")), + false + ); + AzKeyframeStack> rotationFrames = buildKeyframeStack( + getTripletObj(entryObj.get("rotation")), + true + ); + + animations[index] = new AzBoneAnimation(entry.getKey(), rotationFrames, positionFrames, scaleFrames); + index++; + } + + return animations; + } + + /** + * Builds a {@link KeyframeStack} containing keyframes for X, Y, and Z-axis transformations based on the provided + * animation data. The method processes a list of paired time-stamped keyframe data, interprets the JSON structures, + * applies appropriate transformations for rotations (if specified), and generates keyframes with defined easing + * behaviors. + * + * @param entries A list of {@link Pair} objects containing the timestamp as a {@link String} and associated + * {@link JsonElement} data describing the keyframe. Each entry represents a point in time + * within the animation timeline. + * @param isForRotation A boolean indicating whether the keyframe transformations should account for rotation. If + * true, the keyframe values undergo additional processing to convert angles to radians. + * @return A {@link KeyframeStack} containing three lists of keyframes for X, Y, and Z transformations, + * respectively. + * @throws MolangException If an error occurs during the parsing or interpretation of Molang expressions in the + * keyframe data. + */ + private AzKeyframeStack> buildKeyframeStack( + List> entries, + boolean isForRotation + ) throws MolangException { + if (entries.isEmpty()) + return new AzKeyframeStack<>(); + + List> xFrames = new ObjectArrayList<>(); + List> yFrames = new ObjectArrayList<>(); + List> zFrames = new ObjectArrayList<>(); + + IValue xPrev = null; + IValue yPrev = null; + IValue zPrev = null; + Pair prevEntry = null; + + for (Pair entry : entries) { + String key = entry.getFirst(); + JsonElement element = entry.getSecond(); + + if (key.equals("easing") || key.equals("easingArgs") || key.equals("lerp_mode")) + continue; + + double prevTime = prevEntry != null ? Double.parseDouble(prevEntry.getFirst()) : 0; + double curTime = NumberUtils.isCreatable(key) ? Double.parseDouble(entry.getFirst()) : 0; + double timeDelta = curTime - prevTime; + + JsonArray keyframeVector = element instanceof JsonArray array + ? array + : GsonHelper.getAsJsonArray(element.getAsJsonObject(), "vector"); + MolangValue rawXValue = MolangParser.parseJson(keyframeVector.get(0)); + MolangValue rawYValue = MolangParser.parseJson(keyframeVector.get(1)); + MolangValue rawZValue = MolangParser.parseJson(keyframeVector.get(2)); + IValue xValue = isForRotation && rawXValue.isConstant() + ? new Constant(Math.toRadians(-rawXValue.get())) + : rawXValue; + IValue yValue = isForRotation && rawYValue.isConstant() + ? new Constant(Math.toRadians(-rawYValue.get())) + : rawYValue; + IValue zValue = isForRotation && rawZValue.isConstant() + ? new Constant(Math.toRadians(rawZValue.get())) + : rawZValue; + + JsonObject entryObj = element instanceof JsonObject obj ? obj : null; + AzEasingType easingType = entryObj != null && entryObj.has("easing") + ? AzEasingTypeLoader.fromJson(entryObj.get("easing")) + : AzEasingTypes.LINEAR; + List easingArgs = entryObj != null && entryObj.has("easingArgs") + ? JsonUtil.jsonArrayToList( + GsonHelper.getAsJsonArray(entryObj, "easingArgs"), + ele -> new Constant(ele.getAsDouble()) + ) + : new ObjectArrayList<>(); + + xFrames.add( + new AzKeyframe<>(timeDelta * 20, prevEntry == null ? xValue : xPrev, xValue, easingType, easingArgs) + ); + yFrames.add( + new AzKeyframe<>(timeDelta * 20, prevEntry == null ? yValue : yPrev, yValue, easingType, easingArgs) + ); + zFrames.add( + new AzKeyframe<>(timeDelta * 20, prevEntry == null ? zValue : zPrev, zValue, easingType, easingArgs) + ); + + xPrev = xValue; + yPrev = yValue; + zPrev = zValue; + prevEntry = entry; + } + + return new AzKeyframeStack<>(xFrames, yFrames, zFrames); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzKeyframesAdapter.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzKeyframesAdapter.java new file mode 100644 index 000000000..407431523 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzKeyframesAdapter.java @@ -0,0 +1,141 @@ +/** + * This class is a fork of the matching class found in the Geckolib repository. Original source: + * https://github.com/bernie-g/geckolib Copyright © 2024 Bernie-G. Licensed under the MIT License. + * https://github.com/bernie-g/geckolib/blob/main/LICENSE + */ +package mod.azure.azurelib.rewrite.animation.parse; + +import com.google.gson.*; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import mod.azure.azurelib.core.keyframe.event.data.CustomInstructionKeyframeData; +import mod.azure.azurelib.core.keyframe.event.data.ParticleKeyframeData; +import mod.azure.azurelib.core.keyframe.event.data.SoundKeyframeData; +import mod.azure.azurelib.rewrite.animation.primitive.AzKeyframes; +import mod.azure.azurelib.util.JsonUtil; +import net.minecraft.util.GsonHelper; + +import java.lang.reflect.Type; +import java.util.Map; + +/** + * {@link Gson} {@link JsonDeserializer} for {@link AzKeyframes}.
+ * Acts as the deserialization interface for {@code Keyframes} + */ +public class AzKeyframesAdapter implements JsonDeserializer { + + /** + * Builds an array of {@link SoundKeyframeData} objects from a given JSON object. This method parses a JSON object + * containing sound effect data, extracting key-value pairs to create instances of {@link SoundKeyframeData}. The + * keys represent the time in seconds, which are converted into ticks, and the values specify the sound effect. + * + * @param rootObj the root JSON object containing the "sound_effects" data + * @return an array of {@link SoundKeyframeData} objects extracted from the JSON object + */ + private static SoundKeyframeData[] buildSoundFrameData(JsonObject rootObj) { + JsonObject soundsObj = GsonHelper.getAsJsonObject(rootObj, "sound_effects", new JsonObject()); + SoundKeyframeData[] sounds = new SoundKeyframeData[soundsObj.size()]; + int index = 0; + + for (Map.Entry entry : soundsObj.entrySet()) { + sounds[index] = new SoundKeyframeData( + Double.parseDouble(entry.getKey()) * 20d, + GsonHelper.getAsString(entry.getValue().getAsJsonObject(), "effect") + ); + index++; + } + + return sounds; + } + + /** + * Builds an array of {@link ParticleKeyframeData} objects from a given JSON object. This method parses a JSON + * object containing particle effect data, extracting key-value pairs to create instances of + * {@link ParticleKeyframeData}. The keys are interpreted as time in ticks, whereas the values provide effect + * details like effect type, locator, and script. + * + * @param rootObj the root JSON object containing the "particle_effects" data + * @return an array of {@link ParticleKeyframeData} objects extracted from the JSON object + */ + private static ParticleKeyframeData[] buildParticleFrameData(JsonObject rootObj) { + JsonObject particlesObj = GsonHelper.getAsJsonObject(rootObj, "particle_effects", new JsonObject()); + ParticleKeyframeData[] particles = new ParticleKeyframeData[particlesObj.size()]; + int index = 0; + + for (Map.Entry entry : particlesObj.entrySet()) { + JsonObject obj = entry.getValue().getAsJsonObject(); + String effect = GsonHelper.getAsString(obj, "effect", ""); + String locator = GsonHelper.getAsString(obj, "locator", ""); + String script = GsonHelper.getAsString(obj, "pre_effect_script", ""); + + particles[index] = new ParticleKeyframeData( + Double.parseDouble(entry.getKey()) * 20d, + effect, + locator, + script + ); + index++; + } + + return particles; + } + + /** + * Builds an array of {@link CustomInstructionKeyframeData} objects from a given JSON object. + *

+ * This method parses a JSON object containing custom instructions for keyframes, extracting each key-value pair to + * create instances of {@link CustomInstructionKeyframeData}. The keys are interpreted as time in ticks, and the + * values as instructions. + * + * @param rootObj the root JSON object containing the "timeline" data for custom instructions + * @return an array of {@link CustomInstructionKeyframeData} objects extracted from the JSON object + */ + private static CustomInstructionKeyframeData[] buildCustomFrameData(JsonObject rootObj) { + JsonObject customInstructionsObj = GsonHelper.getAsJsonObject(rootObj, "timeline", new JsonObject()); + CustomInstructionKeyframeData[] customInstructions = new CustomInstructionKeyframeData[customInstructionsObj + .size()]; + int index = 0; + + for (Map.Entry entry : customInstructionsObj.entrySet()) { + String instructions = ""; + + if (entry.getValue() instanceof JsonArray array) { + instructions = JsonUtil.GEO_GSON.fromJson(array, ObjectArrayList.class).toString(); + } else if (entry.getValue() instanceof JsonPrimitive primitive) { + instructions = primitive.getAsString(); + } + + customInstructions[index] = new CustomInstructionKeyframeData( + Double.parseDouble(entry.getKey()) * 20d, + instructions + ); + index++; + } + + return customInstructions; + } + + /** + * Deserializes a JSON element into an {@code AzKeyframes} object. This method converts the JSON representation of + * keyframe data into instances of {@code SoundKeyframeData}, {@code ParticleKeyframeData}, and + * {@code CustomInstructionKeyframeData} to construct an {@code AzKeyframes} object. + * + * @param json the JSON element containing the keyframe data + * @param type the type parameter for deserialization (not used in this method) + * @param context the deserialization context provided by Gson + * @return an instance of {@code AzKeyframes} containing deserialized keyframe data + * @throws JsonParseException if the JSON is invalid or cannot be properly parsed + */ + @Override + public AzKeyframes deserialize( + JsonElement json, + Type type, + JsonDeserializationContext context + ) throws JsonParseException { + JsonObject obj = json.getAsJsonObject(); + SoundKeyframeData[] sounds = buildSoundFrameData(obj); + ParticleKeyframeData[] particles = buildParticleFrameData(obj); + CustomInstructionKeyframeData[] customInstructions = buildCustomFrameData(obj); + + return new AzKeyframes(sounds, particles, customInstructions); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehavior.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehavior.java new file mode 100644 index 000000000..86b4875db --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehavior.java @@ -0,0 +1,20 @@ +package mod.azure.azurelib.rewrite.animation.play_behavior; + +import mod.azure.azurelib.rewrite.animation.controller.state.machine.AzAnimationControllerStateMachine; + +public abstract class AzPlayBehavior { + + private final String name; + + protected AzPlayBehavior(String name) { + this.name = name; + } + + public void onUpdate(AzAnimationControllerStateMachine.Context context) {} + + public void onFinish(AzAnimationControllerStateMachine.Context context) {} + + public String name() { + return name; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviorRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviorRegistry.java new file mode 100644 index 000000000..28355a1ca --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviorRegistry.java @@ -0,0 +1,31 @@ +package mod.azure.azurelib.rewrite.animation.play_behavior; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class AzPlayBehaviorRegistry { + + private static final Map PLAY_BEHAVIORS = new HashMap<>(); + + public static AzPlayBehavior register(AzPlayBehavior playBehavior) { + PLAY_BEHAVIORS.put(playBehavior.name(), playBehavior); + return playBehavior; + } + + public static AzPlayBehavior getOrDefault(String name, @NotNull AzPlayBehavior defaultValue) { + return PLAY_BEHAVIORS.getOrDefault(name, defaultValue); + } + + public static @Nullable AzPlayBehavior getOrNull(String name) { + return PLAY_BEHAVIORS.get(name); + } + + public static Collection getValues() { + return Collections.unmodifiableCollection(PLAY_BEHAVIORS.values()); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviors.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviors.java new file mode 100644 index 000000000..65990f9cc --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviors.java @@ -0,0 +1,33 @@ +package mod.azure.azurelib.rewrite.animation.play_behavior; + +import mod.azure.azurelib.rewrite.animation.controller.state.machine.AzAnimationControllerStateMachine; + +public class AzPlayBehaviors { + + public static final AzPlayBehavior HOLD_ON_LAST_FRAME = AzPlayBehaviorRegistry.register(new AzPlayBehavior("hold_on_last_frame") { + @Override + public void onFinish(AzAnimationControllerStateMachine.Context context) { + context.stateMachine().pause(); + } + }); + + public static final AzPlayBehavior LOOP = AzPlayBehaviorRegistry.register(new AzPlayBehavior("loop") { + @Override + public void onFinish(AzAnimationControllerStateMachine.Context context) { + var controller = context.animationController(); + var controllerTimer = controller.controllerTimer(); + var keyframeManager = controller.keyframeManager(); + var keyframeCallbackHandler = keyframeManager.keyframeCallbackHandler(); + + controllerTimer.reset(); + keyframeCallbackHandler.reset(); + } + }); + + public static final AzPlayBehavior PLAY_ONCE = AzPlayBehaviorRegistry.register(new AzPlayBehavior("play_once") { + @Override + public void onFinish(AzAnimationControllerStateMachine.Context context) { + context.stateMachine().stop(); + } + }); +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimation.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimation.java new file mode 100644 index 000000000..89bbf79e3 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimation.java @@ -0,0 +1,19 @@ +package mod.azure.azurelib.rewrite.animation.primitive; + +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzBoneAnimation; + +/** + * A compiled animation instance for use by the {@link AzAnimationController}
+ * Modifications or extensions of a compiled Animation are not supported, and therefore an instance of + * Animation is considered final and immutable. + */ +public record AzBakedAnimation( + String name, + double length, + AzLoopType loopType, + AzBoneAnimation[] boneAnimations, + AzKeyframes keyframes +) { + +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimations.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimations.java new file mode 100644 index 000000000..be05fdd86 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimations.java @@ -0,0 +1,30 @@ +package mod.azure.azurelib.rewrite.animation.primitive; + +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +/** + * Represents a container for baked animations in the AzureLib framework. This record holds mappings for precompiled + * animation instances ({@link AzBakedAnimation}) and resource includes ({@link ResourceLocation}) for use in + * animation-driven content.
+ * The `AzBakedAnimations` structure provides functionality for retrieving animations by name and supporting external + * resource references via the includes mapping, enabling extensibility and reuse of animations across various contexts. + *
+ * Immutable and designed for efficient storage and retrieval of animation data. + */ +public record AzBakedAnimations( + Map animations, + Map includes +) { + + /** + * Gets an {@link AzBakedAnimation} by its name, if present + */ + @Nullable + public AzBakedAnimation getAnimation(String name) { + return animations.get(name); + } + +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzKeyframes.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzKeyframes.java new file mode 100644 index 000000000..7aaa77b39 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzKeyframes.java @@ -0,0 +1,23 @@ +package mod.azure.azurelib.rewrite.animation.primitive; + +import mod.azure.azurelib.core.keyframe.event.data.CustomInstructionKeyframeData; +import mod.azure.azurelib.core.keyframe.event.data.ParticleKeyframeData; +import mod.azure.azurelib.core.keyframe.event.data.SoundKeyframeData; + +/** + * Represents a collection of keyframe data used for animations.
+ * The AzKeyframes record combines different types of keyframe data into a single structure: + *

    + *
  • {@link SoundKeyframeData} for sound-related keyframes.
  • + *
  • {@link ParticleKeyframeData} for particle effect-related keyframes.
  • + *
  • {@link CustomInstructionKeyframeData} for custom instruction keyframes.
  • + *
+ *
+ * This record organizes and provides access to all three types of keyframe data, enabling cohesive handling of + * animation sequences defined in an animation system. + */ +public record AzKeyframes( + SoundKeyframeData[] sounds, + ParticleKeyframeData[] particles, + CustomInstructionKeyframeData[] customInstructions +) {} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzLoopType.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzLoopType.java new file mode 100644 index 000000000..5f49ed224 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzLoopType.java @@ -0,0 +1,151 @@ +package mod.azure.azurelib.rewrite.animation.primitive; + +import com.google.gson.JsonElement; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import org.apache.commons.lang3.function.TriFunction; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Loop type functional interface to define post-play handling for a given animation.
+ * Custom loop types are supported by extending this class and providing the extended class instance as the loop type + * for the animation + * + * @deprecated + */ +@Deprecated(forRemoval = true) +public interface AzLoopType { + + String name(); + + /** + * Override in a custom instance to dynamically decide whether an animation should repeat or stop + * + * @param animatable The animating object relevant to this method call + * @param controller The {@link AzAnimationController} playing the current animation + * @param currentAnimation The current animation that just played + * @return Whether the animation should play again, or stop + */ + boolean shouldPlayAgain( + Object animatable, + AzAnimationController controller, + AzBakedAnimation currentAnimation + ); + + Map LOOP_TYPES = new ConcurrentHashMap<>(5); + + AzLoopType FALSE = register("false", (animatable, controller, currentAnimation) -> false); + + AzLoopType TRUE = register("true", (animatable, controller, currentAnimation) -> true); + + /** + * A predefined {@code AzLoopType} that indicates an animation should play only once without repeating. This loop + * type is used to configure animations that are intended to run a single time and stop when completed, instead of + * looping or holding on the last frame. The associated logic for this type always returns {@code false} for the + * repeat condition, preventing the animation from replaying once it has finished. + */ + AzLoopType PLAY_ONCE = register("play_once", FALSE); + + /** + * A pre-defined AzLoopType representing the behavior of holding on the last frame of an animation after it + * completes. + *

+ * When used as the loop type for an animation, the animation will pause on its final frame. The `pause()` method of + * the controller's state machine ensures this behavior. This is useful when the desired outcome is for the + * animation to display its last frame persistently without replaying or resetting. + *

+ * Implementation Details: - Utilizes the `controller.getStateMachine().pause()` method to halt the animation. - + * Returns `true`, indicating the controller should not proceed to the next state or reset/replay the animation. + */ + AzLoopType HOLD_ON_LAST_FRAME = register("hold_on_last_frame", (animatable, controller, currentAnimation) -> { + controller.stateMachine().pause(); + + return true; + }); + + /** + * Represents a preconfigured {@code AzLoopType} that determines the looping behavior of an animation. The + * {@code LOOP} type is designed to always repeat the animation, ensuring that the looping condition remains + * consistently true. This constant is registered with AzureLib's loop handler using the static {@code register} + * method. When used, it applies a behavior where the animation will indefinitely loop, regardless of any dynamic + * runtime evaluations about the animatable object or animation state. + */ + AzLoopType LOOP = register("loop", TRUE); + + /** + * Retrieve a AzLoopType instance based on a {@link JsonElement}. Returns either {@link AzLoopType#PLAY_ONCE} or + * {@link AzLoopType#LOOP} based on a boolean or string element type, or any other registered loop type with a + * matching type string. + * + * @param json The loop {@link JsonElement} to attempt to parse + * @return A usable AzLoopType instance + */ + static AzLoopType fromJson(JsonElement json) { + if (json == null || !json.isJsonPrimitive()) { + return PLAY_ONCE; + } + + var primitive = json.getAsJsonPrimitive(); + + if (primitive.isBoolean()) { + return primitive.getAsBoolean() ? LOOP : PLAY_ONCE; + } + + if (primitive.isString()) { + return fromString(primitive.getAsString()); + } + + return PLAY_ONCE; + } + + /** + * Retrieves an AzLoopType instance based on the given name. If the name does not match any registered loop type, + * the default {@link AzLoopType#PLAY_ONCE} is returned. + * + * @param name The name of the loop type to retrieve. + * @return The corresponding AzLoopType instance, or the default AzLoopType if no match is found. + */ + static AzLoopType fromString(String name) { + return LOOP_TYPES.getOrDefault(name, PLAY_ONCE); + } + + static AzLoopType register(String name, AzLoopType loopType) { + return register(name, (a, b, c) -> loopType.shouldPlayAgain(a, b, c)); + } + + /** + * Register a AzLoopType with AzureLib for handling loop functionality of animations..
+ * MUST be called during mod construct
+ * It is recommended you don't call this directly, and instead call it via {@code AzureLibUtil#addCustomLoopType} + * + * @param name The name of the loop type + * @param shouldPlayAgainFunction The loop type to register + * @return The registered {@code AzLoopType} + */ + static AzLoopType register( + String name, + TriFunction, AzBakedAnimation, Boolean> shouldPlayAgainFunction + ) { + var loopType = new AzLoopType() { + + @Override + public String name() { + return name; + } + + @Override + public boolean shouldPlayAgain( + Object animatable, + AzAnimationController controller, + AzBakedAnimation currentAnimation + ) { + return shouldPlayAgainFunction.apply(animatable, controller, currentAnimation); + } + }; + + LOOP_TYPES.put(name, loopType); + + return loopType; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzQueuedAnimation.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzQueuedAnimation.java new file mode 100644 index 000000000..436c92450 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzQueuedAnimation.java @@ -0,0 +1,27 @@ +package mod.azure.azurelib.rewrite.animation.primitive; + +import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehavior; + +/** + * Represents an entry in an animation queue, combining an animation and its looping behavior. This record defines a + * queued animation to be played, including its associated {@link AzBakedAnimation} instance and the {@link AzPlayBehavior} + * that determines how the animation behaves once it reaches the end of its sequence.
+ *
+ * Instances of AzQueuedAnimation are immutable by design, ensuring that queued animations, once defined, cannot be + * modified, preserving their behavior within the animation controller.
+ *
+ * Fields: + *

    + *
  • {@code animation}: The {@link AzBakedAnimation} instance that contains the actual animation data to be + * played.
  • + *
  • {@code playBehavior}: The {@link AzPlayBehavior} that dictates the looping behavior or termination handling for the + * animation.
  • + *
+ * + * @deprecated + */ +@Deprecated(forRemoval = true) +public record AzQueuedAnimation( + AzBakedAnimation animation, + AzPlayBehavior playBehavior +) {} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationProperties.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationProperties.java new file mode 100644 index 000000000..f1b6f9a18 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationProperties.java @@ -0,0 +1,91 @@ +package mod.azure.azurelib.rewrite.animation.property; + +import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypes; +import mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationPropertiesCodec; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class AzAnimationProperties { + + public static final AzAnimationPropertiesCodec CODEC = new AzAnimationPropertiesCodec(); + + public static final AzAnimationProperties DEFAULT = new AzAnimationProperties(1D, AzEasingTypes.NONE, 0F); + + public static final AzAnimationProperties EMPTY = new AzAnimationProperties(null, null, null); + + protected final @Nullable Double animationSpeed; + + protected final @Nullable AzEasingType easingType; + + protected final @Nullable Float transitionLength; + + public AzAnimationProperties( + @Nullable Double animationSpeed, + @Nullable AzEasingType easingType, + @Nullable Float transitionLength + ) { + this.animationSpeed = animationSpeed; + this.easingType = easingType; + this.transitionLength = transitionLength; + } + + public boolean hasAnimationSpeed() { + return animationSpeed != null; + } + + public boolean hasEasingType() { + return easingType != null; + } + + public boolean hasTransitionLength() { + return transitionLength != null; + } + + public AzAnimationProperties withAnimationSpeed(double animationSpeed) { + return new AzAnimationProperties(animationSpeed, easingType, transitionLength); + } + + public AzAnimationProperties withEasingType(@NotNull AzEasingType easingType) { + return new AzAnimationProperties(animationSpeed, easingType, transitionLength); + } + + public AzAnimationProperties withTransitionLength(float transitionLength) { + return new AzAnimationProperties(animationSpeed, easingType, transitionLength); + } + + public double animationSpeed() { + return animationSpeed == null ? DEFAULT.animationSpeed() : animationSpeed; + } + + public AzEasingType easingType() { + return easingType == null ? DEFAULT.easingType() : easingType; + } + + public float transitionLength() { + return transitionLength == null ? DEFAULT.transitionLength() : transitionLength; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + AzAnimationProperties that = (AzAnimationProperties) object; + + return Objects.equals(animationSpeed, that.animationSpeed) && Objects.equals(easingType, that.easingType) + && Objects.equals(transitionLength, that.transitionLength); + } + + @Override + public int hashCode() { + return Objects.hash(animationSpeed, easingType, transitionLength); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationStageProperties.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationStageProperties.java new file mode 100644 index 000000000..1432486f8 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationStageProperties.java @@ -0,0 +1,88 @@ +package mod.azure.azurelib.rewrite.animation.property; + +import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypes; +import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehavior; +import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviors; +import mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationStagePropertiesCodec; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class AzAnimationStageProperties extends AzAnimationProperties { + + public static final AzAnimationStagePropertiesCodec CODEC = new AzAnimationStagePropertiesCodec(); + + public static final AzAnimationStageProperties DEFAULT = new AzAnimationStageProperties( + 1D, + AzEasingTypes.NONE, + AzPlayBehaviors.PLAY_ONCE, + 0F + ); + + public static final AzAnimationStageProperties EMPTY = new AzAnimationStageProperties(null, null, null, null); + + private final AzPlayBehavior playBehavior; + + public AzAnimationStageProperties( + @Nullable Double animationSpeed, + @Nullable AzEasingType easingType, + @Nullable AzPlayBehavior playBehavior, + @Nullable Float transitionLength + ) { + super(animationSpeed, easingType, transitionLength); + this.playBehavior = playBehavior; + } + + public boolean hasPlayBehavior() { + return playBehavior != null; + } + + @Override + public AzAnimationStageProperties withAnimationSpeed(double animationSpeed) { + return new AzAnimationStageProperties(animationSpeed, easingType, playBehavior, transitionLength); + } + + @Override + public AzAnimationStageProperties withEasingType(@NotNull AzEasingType easingType) { + return new AzAnimationStageProperties(animationSpeed, easingType, playBehavior, transitionLength); + } + + public AzAnimationStageProperties withPlayBehavior(@NotNull AzPlayBehavior playBehavior) { + return new AzAnimationStageProperties(animationSpeed, easingType, playBehavior, transitionLength); + } + + @Override + public AzAnimationStageProperties withTransitionLength(float transitionLength) { + return new AzAnimationStageProperties(animationSpeed, easingType, playBehavior, transitionLength); + } + + public AzPlayBehavior playBehavior() { + return playBehavior == null ? DEFAULT.playBehavior() : playBehavior; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + if (!super.equals(object)) { + return false; + } + + AzAnimationStageProperties that = (AzAnimationStageProperties) object; + + return Objects.equals(playBehavior, that.playBehavior) && super.equals(object); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), playBehavior); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java new file mode 100644 index 000000000..b12380ecf --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java @@ -0,0 +1,54 @@ +package mod.azure.azurelib.rewrite.animation.property.codec; + +import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypeRegistry; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypes; +import mod.azure.azurelib.rewrite.animation.property.AzAnimationProperties; +import net.minecraft.network.FriendlyByteBuf; +import org.jetbrains.annotations.NotNull; + +public class AzAnimationPropertiesCodec { + + public @NotNull AzAnimationProperties decode(FriendlyByteBuf buf) { + var propertyLength = buf.readByte(); + var properties = AzAnimationProperties.EMPTY; + + for (int i = 0; i < propertyLength; i++) { + var code = buf.readByte(); + + switch (code) { + case 0 -> properties = properties.withAnimationSpeed(buf.readDouble()); + case 1 -> properties = properties.withTransitionLength(buf.readFloat()); + case 2 -> { + var easingType = AzEasingTypeRegistry.getOrDefault(buf.readUtf(), AzEasingTypes.NONE); + properties = properties.withEasingType(easingType); + } + } + } + + return properties; + } + + public void encode(FriendlyByteBuf buf, AzAnimationProperties properties) { + var propertyLength = 0; + propertyLength += properties.hasAnimationSpeed() ? 1 : 0; + propertyLength += properties.hasTransitionLength() ? 1 : 0; + propertyLength += properties.hasEasingType() ? 1 : 0; + + buf.writeByte(propertyLength); + + if (properties.hasAnimationSpeed()) { + buf.writeByte(0); + buf.writeDouble(properties.animationSpeed()); + } + + if (properties.hasTransitionLength()) { + buf.writeByte(1); + buf.writeFloat(properties.transitionLength()); + } + + if (properties.hasEasingType()) { + buf.writeByte(2); + buf.writeUtf(properties.easingType().name()); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java new file mode 100644 index 000000000..044d47b96 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java @@ -0,0 +1,66 @@ +package mod.azure.azurelib.rewrite.animation.property.codec; + +import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypeRegistry; +import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypes; +import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviorRegistry; +import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviors; +import mod.azure.azurelib.rewrite.animation.property.AzAnimationStageProperties; +import net.minecraft.network.FriendlyByteBuf; +import org.jetbrains.annotations.NotNull; + +public class AzAnimationStagePropertiesCodec { + + public @NotNull AzAnimationStageProperties decode(FriendlyByteBuf buf) { + var propertyLength = buf.readByte(); + var properties = AzAnimationStageProperties.EMPTY; + + for (int i = 0; i < propertyLength; i++) { + var code = buf.readByte(); + + switch (code) { + case 0 -> properties = properties.withAnimationSpeed(buf.readDouble()); + case 1 -> properties = properties.withTransitionLength(buf.readFloat()); + case 2 -> { + var easingType = AzEasingTypeRegistry.getOrDefault(buf.readUtf(), AzEasingTypes.NONE); + properties = properties.withEasingType(easingType); + } + case 3 -> { + var playBehavior = AzPlayBehaviorRegistry.getOrDefault(buf.readUtf(), AzPlayBehaviors.PLAY_ONCE); + properties = properties.withPlayBehavior(playBehavior); + } + } + } + + return properties; + } + + public void encode(FriendlyByteBuf buf, AzAnimationStageProperties properties) { + var propertyLength = 0; + propertyLength += properties.hasAnimationSpeed() ? 1 : 0; + propertyLength += properties.hasTransitionLength() ? 1 : 0; + propertyLength += properties.hasEasingType() ? 1 : 0; + propertyLength += properties.hasPlayBehavior() ? 1 : 0; + + buf.writeByte(propertyLength); + + if (properties.hasAnimationSpeed()) { + buf.writeByte(0); + buf.writeDouble(properties.animationSpeed()); + } + + if (properties.hasTransitionLength()) { + buf.writeByte(1); + buf.writeFloat(properties.transitionLength()); + } + + if (properties.hasEasingType()) { + buf.writeByte(2); + buf.writeUtf(properties.easingType().name()); + } + + if (properties.hasPlayBehavior()) { + buf.writeByte(3); + buf.writeUtf(properties.playBehavior().name()); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBakedModel.java b/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBakedModel.java new file mode 100644 index 000000000..40c23f402 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBakedModel.java @@ -0,0 +1,53 @@ +package mod.azure.azurelib.rewrite.model; + +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Represents a baked 3D model consisting of hierarchical bone structures. This class is immutable and provides + * read-only access to bones by name or as a list of top-level bones. Bones are uniquely identified by their names. + */ +public class AzBakedModel { + + public static final AzBakedModel EMPTY = new AzBakedModel(List.of()); + + private final Map bonesByName; + + private final List topLevelBones; + + public AzBakedModel(List topLevelBones) { + this.topLevelBones = Collections.unmodifiableList(topLevelBones); + this.bonesByName = Collections.unmodifiableMap(mapBonesByName(topLevelBones)); + } + + private Map mapBonesByName(List bones) { + var bonesByName = new HashMap(); + var nodesToMap = new ArrayDeque<>(bones); + + while (!nodesToMap.isEmpty()) { + var currentBone = nodesToMap.poll(); + nodesToMap.addAll(currentBone.getChildBones()); + currentBone.saveInitialSnapshot(); + bonesByName.put(currentBone.getName(), currentBone); + } + + return bonesByName; + } + + public @Nullable AzBone getBoneOrNull(String name) { + return bonesByName.get(name); + } + + public Optional getBone(String name) { + return Optional.ofNullable(getBoneOrNull(name)); + } + + public Map getBonesByName() { + return bonesByName; + } + + public List getTopLevelBones() { + return topLevelBones; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBone.java b/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBone.java new file mode 100644 index 000000000..863309916 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBone.java @@ -0,0 +1,459 @@ +/** + * This class is a fork of the matching class found in the Geckolib repository. Original source: + * https://github.com/bernie-g/geckolib Copyright © 2024 Bernie-G. Licensed under the MIT License. + * https://github.com/bernie-g/geckolib/blob/main/LICENSE + */ +package mod.azure.azurelib.rewrite.model; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import mod.azure.azurelib.cache.object.GeoCube; +import mod.azure.azurelib.core.animatable.model.CoreGeoBone; +import mod.azure.azurelib.core.state.BoneSnapshot; +import org.joml.*; + +import java.util.List; +import java.util.Objects; + +/** + * Mutable bone object representing a set of cubes, as well as child bones.
+ * This is the object that is directly modified by animations to handle movement + */ +public class AzBone implements CoreGeoBone { + + private final AzBoneMetadata metadata; + + private final List children = new ObjectArrayList<>(); + + private final List cubes = new ObjectArrayList<>(); + + private final Matrix4f modelSpaceMatrix = new Matrix4f(); + + private final Matrix4f localSpaceMatrix = new Matrix4f(); + + private final Matrix4f worldSpaceMatrix = new Matrix4f(); + + private AzBoneSnapshot initialSnapshot; + + private boolean hidden; + + private boolean childrenHidden = false; + + private final Vector3f pivot; + + private final Vector3f position; + + private final Vector3f rotation; + + private final Vector3f scale; + + private boolean positionChanged = false; + + private boolean rotationChanged = false; + + private boolean scaleChanged = false; + + private Matrix3f worldSpaceNormal = new Matrix3f(); + + private boolean trackingMatrices; + + public AzBone(AzBoneMetadata metadata) { + this.metadata = metadata; + this.trackingMatrices = false; + this.hidden = metadata.dontRender() == Boolean.TRUE; + + this.position = new Vector3f(); + this.pivot = new Vector3f(); + this.rotation = new Vector3f(); + this.scale = new Vector3f(1, 1, 1); + + this.worldSpaceNormal.identity(); + this.worldSpaceMatrix.identity(); + this.localSpaceMatrix.identity(); + this.modelSpaceMatrix.identity(); + } + + @Override + public String getName() { + return metadata.name(); + } + + @Override + public AzBone getParent() { + return metadata.parent(); + } + + @Override + public float getRotX() { + return this.rotation.x; + } + + @Override + public void setRotX(float value) { + this.rotation.x = value; + + markRotationAsChanged(); + } + + @Override + public float getRotY() { + return this.rotation.y; + } + + @Override + public void setRotY(float value) { + this.rotation.y = value; + + markRotationAsChanged(); + } + + @Override + public float getRotZ() { + return this.rotation.z; + } + + @Override + public void setRotZ(float value) { + this.rotation.z = value; + + markRotationAsChanged(); + } + + @Override + public float getPosX() { + return this.position.x; + } + + @Override + public void setPosX(float value) { + this.position.x = value; + + markPositionAsChanged(); + } + + @Override + public float getPosY() { + return this.position.y; + } + + @Override + public void setPosY(float value) { + this.position.y = value; + + markPositionAsChanged(); + } + + @Override + public float getPosZ() { + return this.position.z; + } + + @Override + public void setPosZ(float value) { + this.position.z = value; + + markPositionAsChanged(); + } + + @Override + public float getScaleX() { + return this.scale.x; + } + + @Override + public void setScaleX(float value) { + this.scale.x = value; + + markScaleAsChanged(); + } + + @Override + public float getScaleY() { + return this.scale.y; + } + + @Override + public void setScaleY(float value) { + this.scale.y = value; + + markScaleAsChanged(); + } + + @Override + public float getScaleZ() { + return this.scale.z; + } + + @Override + public void setScaleZ(float value) { + this.scale.z = value; + + markScaleAsChanged(); + } + + @Override + public boolean isHidden() { + return this.hidden; + } + + @Override + public void setHidden(boolean hidden) { + this.hidden = hidden; + + setChildrenHidden(hidden); + } + + @Override + public void setChildrenHidden(boolean hideChildren) { + this.childrenHidden = hideChildren; + } + + @Override + public float getPivotX() { + return this.pivot.x; + } + + @Override + public void setPivotX(float value) { + this.pivot.x = value; + } + + @Override + public float getPivotY() { + return this.pivot.y; + } + + @Override + public void setPivotY(float value) { + this.pivot.y = value; + } + + @Override + public float getPivotZ() { + return this.pivot.z; + } + + @Override + public void setPivotZ(float value) { + this.pivot.z = value; + } + + @Override + public boolean isHidingChildren() { + return this.childrenHidden; + } + + @Override + public void markScaleAsChanged() { + this.scaleChanged = true; + } + + @Override + public void markRotationAsChanged() { + this.rotationChanged = true; + } + + @Override + public void markPositionAsChanged() { + this.positionChanged = true; + } + + @Override + public boolean hasScaleChanged() { + return this.scaleChanged; + } + + @Override + public boolean hasRotationChanged() { + return this.rotationChanged; + } + + @Override + public boolean hasPositionChanged() { + return this.positionChanged; + } + + @Override + public void resetStateChanges() { + this.scaleChanged = false; + this.rotationChanged = false; + this.positionChanged = false; + } + + /** + * @deprecated DO NOT USE OR I WILL FIND YOU. + */ + @Override + @Deprecated(forRemoval = true) + public BoneSnapshot getInitialSnapshot() { + throw new UnsupportedOperationException(); + } + + public AzBoneSnapshot getInitialAzSnapshot() { + return this.initialSnapshot; + } + + @Override + public List getChildBones() { + return this.children; + } + + @Override + public void saveInitialSnapshot() { + if (this.initialSnapshot == null) { + this.initialSnapshot = new AzBoneSnapshot(this); + } + } + + public Boolean getMirror() { + return metadata.mirror(); + } + + public Double getInflate() { + return metadata.inflate(); + } + + public Boolean shouldNeverRender() { + return metadata.dontRender(); + } + + public Boolean getReset() { + return metadata.reset(); + } + + public List getCubes() { + return this.cubes; + } + + public boolean isTrackingMatrices() { + return trackingMatrices; + } + + public void setTrackingMatrices(boolean trackingMatrices) { + this.trackingMatrices = trackingMatrices; + } + + public Matrix4f getModelSpaceMatrix() { + setTrackingMatrices(true); + + return this.modelSpaceMatrix; + } + + public void setModelSpaceMatrix(Matrix4f matrix) { + this.modelSpaceMatrix.set(matrix); + } + + public Matrix4f getLocalSpaceMatrix() { + setTrackingMatrices(true); + + return this.localSpaceMatrix; + } + + public void setLocalSpaceMatrix(Matrix4f matrix) { + this.localSpaceMatrix.set(matrix); + } + + public Matrix4f getWorldSpaceMatrix() { + setTrackingMatrices(true); + + return this.worldSpaceMatrix; + } + + public void setWorldSpaceMatrix(Matrix4f matrix) { + this.worldSpaceMatrix.set(matrix); + } + + public Matrix3f getWorldSpaceNormal() { + return worldSpaceNormal; + } + + public void setWorldSpaceNormal(Matrix3f matrix) { + this.worldSpaceNormal = matrix; + } + + /** + * Get the position of the bone relative to its owner + */ + public Vector3d getLocalPosition() { + Vector4f vec = getLocalSpaceMatrix().transform(new Vector4f(0, 0, 0, 1)); + + return new Vector3d(vec.x(), vec.y(), vec.z()); + } + + /** + * Get the position of the bone relative to the model it belongs to + */ + public Vector3d getModelPosition() { + Vector4f vec = getModelSpaceMatrix().transform(new Vector4f(0, 0, 0, 1)); + + return new Vector3d(-vec.x() * 16f, vec.y() * 16f, vec.z() * 16f); + } + + public void setModelPosition(Vector3d pos) { + // Doesn't work on bones with parent transforms + AzBone parent = metadata.parent(); + Matrix4f matrix = (parent == null ? new Matrix4f().identity() : new Matrix4f(parent.getModelSpaceMatrix())) + .invert(); + Vector4f vec = matrix.transform( + new Vector4f(-(float) pos.x / 16f, (float) pos.y / 16f, (float) pos.z / 16f, 1) + ); + + updatePosition(-vec.x() * 16f, vec.y() * 16f, vec.z() * 16f); + } + + /** + * Get the position of the bone relative to the world + */ + public Vector3d getWorldPosition() { + Vector4f vec = getWorldSpaceMatrix().transform(new Vector4f(0, 0, 0, 1)); + + return new Vector3d(vec.x(), vec.y(), vec.z()); + } + + public Matrix4f getModelRotationMatrix() { + Matrix4f matrix = new Matrix4f(getModelSpaceMatrix()); + matrix.m03(0); + matrix.m13(0); + matrix.m23(0); + + return matrix; + } + + public Vector3d getPositionVector() { + return new Vector3d(getPosX(), getPosY(), getPosZ()); + } + + public Vector3d getRotationVector() { + return new Vector3d(getRotX(), getRotY(), getRotZ()); + } + + public Vector3d getScaleVector() { + return new Vector3d(getScaleX(), getScaleY(), getScaleZ()); + } + + public void addRotationOffsetFromBone(AzBone source) { + setRotX(getRotX() + source.getRotX() - source.getInitialAzSnapshot().getRotX()); + setRotY(getRotY() + source.getRotY() - source.getInitialAzSnapshot().getRotY()); + setRotZ(getRotZ() + source.getRotZ() - source.getInitialAzSnapshot().getRotZ()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (obj == null || getClass() != obj.getClass()) + return false; + + return hashCode() == obj.hashCode(); + } + + @Override + public int hashCode() { + return Objects.hash( + getName(), + (getParent() != null ? getParent().getName() : 0), + getCubes().size(), + getChildBones().size() + ); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneMetadata.java b/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneMetadata.java new file mode 100644 index 000000000..6afa3c3e8 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneMetadata.java @@ -0,0 +1,23 @@ +package mod.azure.azurelib.rewrite.model; + +import mod.azure.azurelib.loading.json.raw.Bone; +import org.jetbrains.annotations.Nullable; + +/** + * AzBoneMetadata is a record class representing metadata about a 3D model bone. This metadata provides information such + * as rendering preferences, inflation values, mirroring, hierarchy, and reset options for a bone in a 3D model's + * structure. + */ +public record AzBoneMetadata( + @Nullable Boolean dontRender, + @Nullable Double inflate, + Boolean mirror, + String name, + @Nullable AzBone parent, + @Nullable Boolean reset +) { + + public AzBoneMetadata(Bone bone, AzBone parent) { + this(bone.neverRender(), bone.inflate(), bone.mirror(), bone.name(), parent, bone.reset()); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneSnapshot.java b/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneSnapshot.java new file mode 100644 index 000000000..80ad60909 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneSnapshot.java @@ -0,0 +1,180 @@ +/** + * This class is a fork of the matching class found in the Geckolib repository. Original source: + * https://github.com/bernie-g/geckolib Copyright © 2024 Bernie-G. Licensed under the MIT License. + * https://github.com/bernie-g/geckolib/blob/main/LICENSE + */ + +package mod.azure.azurelib.rewrite.model; + +import org.joml.Vector3f; + +/** + * A state monitoring class for a given {@link AzBone}.
+ */ +public class AzBoneSnapshot { + + private final AzBone bone; + + private final Vector3f offsetPosition; + + private final Vector3f rotation; + + private final Vector3f scale; + + private double lastResetRotationTick = 0; + + private double lastResetPositionTick = 0; + + private double lastResetScaleTick = 0; + + private boolean rotAnimInProgress = true; + + private boolean posAnimInProgress = true; + + private boolean scaleAnimInProgress = true; + + public AzBoneSnapshot(AzBone bone) { + this.bone = bone; + this.offsetPosition = new Vector3f(bone.getPosX(), bone.getPosY(), bone.getPosZ()); + this.rotation = new Vector3f(bone.getRotX(), bone.getRotY(), bone.getRotZ()); + this.scale = new Vector3f(bone.getScaleX(), bone.getScaleY(), bone.getScaleZ()); + } + + public static AzBoneSnapshot copy(AzBoneSnapshot snapshot) { + AzBoneSnapshot newSnapshot = new AzBoneSnapshot(snapshot.bone); + + newSnapshot.offsetPosition.set(snapshot.offsetPosition); + newSnapshot.rotation.set(snapshot.rotation); + newSnapshot.scale.set(snapshot.scale); + + return newSnapshot; + } + + public AzBone getBone() { + return this.bone; + } + + public float getScaleX() { + return this.scale.x; + } + + public float getScaleY() { + return this.scale.y; + } + + public float getScaleZ() { + return this.scale.z; + } + + public float getOffsetX() { + return this.offsetPosition.x; + } + + public float getOffsetY() { + return this.offsetPosition.y; + } + + public float getOffsetZ() { + return this.offsetPosition.z; + } + + public float getRotX() { + return this.rotation.x; + } + + public float getRotY() { + return this.rotation.y; + } + + public float getRotZ() { + return this.rotation.z; + } + + public double getLastResetRotationTick() { + return this.lastResetRotationTick; + } + + public double getLastResetPositionTick() { + return this.lastResetPositionTick; + } + + public double getLastResetScaleTick() { + return this.lastResetScaleTick; + } + + public boolean isRotAnimInProgress() { + return this.rotAnimInProgress; + } + + public boolean isPosAnimInProgress() { + return this.posAnimInProgress; + } + + public boolean isScaleAnimInProgress() { + return this.scaleAnimInProgress; + } + + /** + * Update the scale state of this snapshot + */ + public void updateScale(float scaleX, float scaleY, float scaleZ) { + scale.set(scaleX, scaleY, scaleZ); + } + + /** + * Update the offset state of this snapshot + */ + public void updateOffset(float offsetX, float offsetY, float offsetZ) { + offsetPosition.set(offsetX, offsetY, offsetZ); + } + + /** + * Update the rotation state of this snapshot + */ + public void updateRotation(float rotX, float rotY, float rotZ) { + rotation.set(rotX, rotY, rotZ); + } + + public void startPosAnim() { + this.posAnimInProgress = true; + } + + public void stopPosAnim(double tick) { + this.posAnimInProgress = false; + this.lastResetPositionTick = tick; + } + + public void startRotAnim() { + this.rotAnimInProgress = true; + } + + public void stopRotAnim(double tick) { + this.rotAnimInProgress = false; + this.lastResetRotationTick = tick; + } + + public void startScaleAnim() { + this.scaleAnimInProgress = true; + } + + public void stopScaleAnim(double tick) { + this.scaleAnimInProgress = false; + this.lastResetScaleTick = tick; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (obj == null || getClass() != obj.getClass()) + return false; + + return hashCode() == obj.hashCode(); + } + + @Override + public int hashCode() { + return this.bone.getName().hashCode(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/model/cache/AzBakedModelCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/model/cache/AzBakedModelCache.java new file mode 100644 index 000000000..ce1069103 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/model/cache/AzBakedModelCache.java @@ -0,0 +1,49 @@ +package mod.azure.azurelib.rewrite.model.cache; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mod.azure.azurelib.loading.FileLoader; +import mod.azure.azurelib.loading.json.raw.Model; +import mod.azure.azurelib.loading.object.GeometryTree; +import mod.azure.azurelib.rewrite.AzResourceCache; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.model.factory.registry.AzBakedModelFactoryRegistry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * AzBakedModelCache is a singleton class that extends {@link AzResourceCache} and is designed to manage and cache baked + * models of type {@link AzBakedModel}. It provides functionality to asynchronously load and store models associated + * with specific resource locations. + */ +public class AzBakedModelCache extends AzResourceCache { + + private static final AzBakedModelCache INSTANCE = new AzBakedModelCache(); + + public static AzBakedModelCache getInstance() { + return INSTANCE; + } + + private final Map bakedModels; + + private AzBakedModelCache() { + this.bakedModels = new Object2ObjectOpenHashMap<>(); + } + + public CompletableFuture loadModels(Executor backgroundExecutor, ResourceManager resourceManager) { + return loadResources(backgroundExecutor, resourceManager, "geo", resource -> { + Model model = FileLoader.loadModelFile(resource, resourceManager); + + return AzBakedModelFactoryRegistry.getForNamespace(resource.getNamespace()) + .constructGeoModel(GeometryTree.fromModel(model)); + }, bakedModels::put); + } + + public @Nullable AzBakedModel getNullable(ResourceLocation resourceLocation) { + return bakedModels.get(resourceLocation); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/AzBakedModelFactory.java b/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/AzBakedModelFactory.java new file mode 100644 index 000000000..08bec00f3 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/AzBakedModelFactory.java @@ -0,0 +1,147 @@ +package mod.azure.azurelib.rewrite.model.factory; + +import mod.azure.azurelib.cache.object.GeoCube; +import mod.azure.azurelib.cache.object.GeoQuad; +import mod.azure.azurelib.loading.json.raw.Cube; +import mod.azure.azurelib.loading.json.raw.FaceUV; +import mod.azure.azurelib.loading.json.raw.ModelProperties; +import mod.azure.azurelib.loading.json.raw.UVUnion; +import mod.azure.azurelib.loading.object.BoneStructure; +import mod.azure.azurelib.loading.object.GeometryTree; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.model.factory.primitive.VertexSet; +import net.minecraft.core.Direction; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; + +/** + * Abstract factory class for constructing baked models, bones, and cubes from raw input data such as geometry trees or + * properties. This class provides a structure for defining the creation of complex 3D models, including managing + * hierarchical relationships between bones and their associated components. + */ +public abstract class AzBakedModelFactory { + + /** + * Construct the output model from the given {@link GeometryTree}.
+ */ + public abstract AzBakedModel constructGeoModel(GeometryTree geometryTree); + + /** + * Construct a {@link AzBone} from the relevant raw input data + * + * @param boneStructure The {@code BoneStructure} comprising the structure of the bone and its children + * @param properties The loaded properties for the model + * @param parent The parent bone for this bone, or null if a top-level bone + */ + public abstract AzBone constructBone( + BoneStructure boneStructure, + ModelProperties properties, + @Nullable AzBone parent + ); + + /** + * Construct a {@link GeoCube} from the relevant raw input data + * + * @param cube The raw {@code Cube} comprising the structure and properties of the cube + * @param properties The loaded properties for the model + * @param bone The bone this cube belongs to + */ + public abstract GeoCube constructCube(Cube cube, ModelProperties properties, AzBone bone); + + /** + * Builtin method to construct the quad list from the various vertices and related data, to make it easier.
+ * Vertices have already been mirrored here if {@code mirror} is true + */ + public GeoQuad[] buildQuads( + UVUnion uvUnion, + VertexSet vertices, + Cube cube, + float textureWidth, + float textureHeight, + boolean mirror + ) { + GeoQuad[] quads = new GeoQuad[6]; + + quads[0] = buildQuad(vertices, cube, uvUnion, textureWidth, textureHeight, mirror, Direction.WEST); + quads[1] = buildQuad(vertices, cube, uvUnion, textureWidth, textureHeight, mirror, Direction.EAST); + quads[2] = buildQuad(vertices, cube, uvUnion, textureWidth, textureHeight, mirror, Direction.NORTH); + quads[3] = buildQuad(vertices, cube, uvUnion, textureWidth, textureHeight, mirror, Direction.SOUTH); + quads[4] = buildQuad(vertices, cube, uvUnion, textureWidth, textureHeight, mirror, Direction.UP); + quads[5] = buildQuad(vertices, cube, uvUnion, textureWidth, textureHeight, mirror, Direction.DOWN); + + return quads; + } + + /** + * Build an individual quad + */ + public GeoQuad buildQuad( + VertexSet vertices, + Cube cube, + UVUnion uvUnion, + float textureWidth, + float textureHeight, + boolean mirror, + Direction direction + ) { + if (!uvUnion.isBoxUV()) { + FaceUV faceUV = uvUnion.faceUV().fromDirection(direction); + + if (faceUV == null) + return null; + + return GeoQuad.build( + vertices.verticesForQuad(direction, false, mirror || cube.mirror() == Boolean.TRUE), + faceUV.uv(), + faceUV.uvSize(), + faceUV.uvRotation(), + textureWidth, + textureHeight, + mirror, + direction + ); + } + + double[] uv = cube.uv().boxUVCoords(); + double[] uvSize = cube.size(); + Vec3 uvSizeVec = new Vec3(Math.floor(uvSize[0]), Math.floor(uvSize[1]), Math.floor(uvSize[2])); + double[][] uvData = switch (direction) { + case WEST -> new double[][] { + new double[] { uv[0] + uvSizeVec.z + uvSizeVec.x, uv[1] + uvSizeVec.z }, + new double[] { uvSizeVec.z, uvSizeVec.y } + }; + case EAST -> new double[][] { + new double[] { uv[0], uv[1] + uvSizeVec.z }, + new double[] { uvSizeVec.z, uvSizeVec.y } + }; + case NORTH -> new double[][] { + new double[] { uv[0] + uvSizeVec.z, uv[1] + uvSizeVec.z }, + new double[] { uvSizeVec.x, uvSizeVec.y } + }; + case SOUTH -> new double[][] { + new double[] { uv[0] + uvSizeVec.z + uvSizeVec.x + uvSizeVec.z, uv[1] + uvSizeVec.z }, + new double[] { uvSizeVec.x, uvSizeVec.y } + }; + case UP -> new double[][] { + new double[] { uv[0] + uvSizeVec.z, uv[1] }, + new double[] { uvSizeVec.x, uvSizeVec.z } + }; + case DOWN -> new double[][] { + new double[] { uv[0] + uvSizeVec.z + uvSizeVec.x, uv[1] + uvSizeVec.z }, + new double[] { uvSizeVec.x, -uvSizeVec.z } + }; + }; + + return GeoQuad.build( + vertices.verticesForQuad(direction, true, mirror || cube.mirror() == Boolean.TRUE), + uvData[0], + uvData[1], + FaceUV.Rotation.NONE, + textureWidth, + textureHeight, + mirror, + direction + ); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/impl/AzBuiltinBakedModelFactory.java b/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/impl/AzBuiltinBakedModelFactory.java new file mode 100644 index 000000000..86e3c68fe --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/impl/AzBuiltinBakedModelFactory.java @@ -0,0 +1,88 @@ +package mod.azure.azurelib.rewrite.model.factory.impl; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import mod.azure.azurelib.cache.object.GeoCube; +import mod.azure.azurelib.loading.json.raw.Cube; +import mod.azure.azurelib.loading.json.raw.ModelProperties; +import mod.azure.azurelib.loading.object.BoneStructure; +import mod.azure.azurelib.loading.object.GeometryTree; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.model.AzBoneMetadata; +import mod.azure.azurelib.rewrite.model.factory.AzBakedModelFactory; +import mod.azure.azurelib.rewrite.model.factory.primitive.VertexSet; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.world.phys.Vec3; + +/** + * A concrete implementation of the {@link AzBakedModelFactory} that constructs baked models, bones, and cubes from raw + * geometry data. It is tailored to create and configure the model and its associated components in a hierarchical + * manner, based on the provided structure and properties. + */ +public final class AzBuiltinBakedModelFactory extends AzBakedModelFactory { + + @Override + public AzBakedModel constructGeoModel(GeometryTree geometryTree) { + var bones = new ObjectArrayList(); + + for (var boneStructure : geometryTree.topLevelBones().values()) { + bones.add(constructBone(boneStructure, geometryTree.properties(), null)); + } + + return new AzBakedModel(bones); + } + + @Override + public AzBone constructBone(BoneStructure boneStructure, ModelProperties properties, AzBone parent) { + var bone = boneStructure.self(); + var boneMetadata = new AzBoneMetadata(bone, parent); + var newBone = new AzBone(boneMetadata); + var rotation = RenderUtils.arrayToVec(bone.rotation()); + var pivot = RenderUtils.arrayToVec(bone.pivot()); + + newBone.updateRotation( + (float) Math.toRadians(-rotation.x), + (float) Math.toRadians(-rotation.y), + (float) Math.toRadians(rotation.z) + ); + newBone.updatePivot((float) -pivot.x, (float) pivot.y, (float) pivot.z); + + for (var cube : bone.cubes()) { + newBone.getCubes().add(constructCube(cube, properties, newBone)); + } + + // TODO: Avoid recursive calls here. + for (var child : boneStructure.children().values()) { + newBone.getChildBones().add(constructBone(child, properties, newBone)); + } + + return newBone; + } + + @Override + public GeoCube constructCube(Cube cube, ModelProperties properties, AzBone bone) { + var mirror = cube.mirror() == Boolean.TRUE; + var inflate = cube.inflate() != null + ? cube.inflate() / 16f + : (bone.getInflate() == null ? 0 : bone.getInflate() / 16f); + var size = RenderUtils.arrayToVec(cube.size()); + var origin = RenderUtils.arrayToVec(cube.origin()); + var rotation = RenderUtils.arrayToVec(cube.rotation()); + var pivot = RenderUtils.arrayToVec(cube.pivot()); + origin = new Vec3(-(origin.x + size.x) / 16d, origin.y / 16d, origin.z / 16d); + var vertexSize = size.multiply(1 / 16d, 1 / 16d, 1 / 16d); + + pivot = pivot.multiply(-1, 1, 1); + rotation = new Vec3(Math.toRadians(-rotation.x), Math.toRadians(-rotation.y), Math.toRadians(rotation.z)); + var quads = buildQuads( + cube.uv(), + new VertexSet(origin, vertexSize, inflate), + cube, + (float) properties.textureWidth(), + (float) properties.textureHeight(), + mirror + ); + + return new GeoCube(quads, pivot, rotation, size, inflate, mirror); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/primitive/VertexSet.java b/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/primitive/VertexSet.java new file mode 100644 index 000000000..8cd38c448 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/primitive/VertexSet.java @@ -0,0 +1,120 @@ +package mod.azure.azurelib.rewrite.model.factory.primitive; + +import mod.azure.azurelib.cache.object.GeoVertex; +import net.minecraft.core.Direction; +import net.minecraft.world.phys.Vec3; + +/** + * Holder class to make it easier to store and refer to vertices for a given cube + */ +public record VertexSet( + GeoVertex bottomLeftBack, + GeoVertex bottomRightBack, + GeoVertex topLeftBack, + GeoVertex topRightBack, + GeoVertex topLeftFront, + GeoVertex topRightFront, + GeoVertex bottomLeftFront, + GeoVertex bottomRightFront +) { + + public VertexSet(Vec3 origin, Vec3 vertexSize, double inflation) { + this( + new GeoVertex(origin.x - inflation, origin.y - inflation, origin.z - inflation), + new GeoVertex(origin.x - inflation, origin.y - inflation, origin.z + vertexSize.z + inflation), + new GeoVertex(origin.x - inflation, origin.y + vertexSize.y + inflation, origin.z - inflation), + new GeoVertex( + origin.x - inflation, + origin.y + vertexSize.y + inflation, + origin.z + vertexSize.z + inflation + ), + new GeoVertex( + origin.x + vertexSize.x + inflation, + origin.y + vertexSize.y + inflation, + origin.z - inflation + ), + new GeoVertex( + origin.x + vertexSize.x + inflation, + origin.y + vertexSize.y + inflation, + origin.z + vertexSize.z + inflation + ), + new GeoVertex(origin.x + vertexSize.x + inflation, origin.y - inflation, origin.z - inflation), + new GeoVertex( + origin.x + vertexSize.x + inflation, + origin.y - inflation, + origin.z + vertexSize.z + inflation + ) + ); + } + + /** + * Returns the normal vertex array for a west-facing quad + */ + public GeoVertex[] quadWest() { + return new GeoVertex[] { this.topRightBack, this.topLeftBack, this.bottomLeftBack, this.bottomRightBack }; + } + + /** + * Returns the normal vertex array for an east-facing quad + */ + public GeoVertex[] quadEast() { + return new GeoVertex[] { + this.topLeftFront, + this.topRightFront, + this.bottomRightFront, + this.bottomLeftFront + }; + } + + /** + * Returns the normal vertex array for a north-facing quad + */ + public GeoVertex[] quadNorth() { + return new GeoVertex[] { this.topLeftBack, this.topLeftFront, this.bottomLeftFront, this.bottomLeftBack }; + } + + /** + * Returns the normal vertex array for a south-facing quad + */ + public GeoVertex[] quadSouth() { + return new GeoVertex[] { + this.topRightFront, + this.topRightBack, + this.bottomRightBack, + this.bottomRightFront + }; + } + + /** + * Returns the normal vertex array for a top-facing quad + */ + public GeoVertex[] quadUp() { + return new GeoVertex[] { this.topRightBack, this.topRightFront, this.topLeftFront, this.topLeftBack }; + } + + /** + * Returns the normal vertex array for a bottom-facing quad + */ + public GeoVertex[] quadDown() { + return new GeoVertex[] { + this.bottomLeftBack, + this.bottomLeftFront, + this.bottomRightFront, + this.bottomRightBack + }; + } + + /** + * Return the vertex array relevant to the quad being built, taking into account mirroring and quad type + */ + public GeoVertex[] verticesForQuad(Direction direction, boolean boxUv, boolean mirror) { + return switch (direction) { + case WEST -> mirror ? quadEast() : quadWest(); + case EAST -> mirror ? quadWest() : quadEast(); + case NORTH -> quadNorth(); + case SOUTH -> quadSouth(); + case UP -> mirror && !boxUv ? quadDown() : quadUp(); + case DOWN -> mirror && !boxUv ? quadUp() : quadDown(); + }; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/registry/AzBakedModelFactoryRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/registry/AzBakedModelFactoryRegistry.java new file mode 100644 index 000000000..200d12c56 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/model/factory/registry/AzBakedModelFactoryRegistry.java @@ -0,0 +1,38 @@ +package mod.azure.azurelib.rewrite.model.factory.registry; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mod.azure.azurelib.rewrite.model.factory.AzBakedModelFactory; +import mod.azure.azurelib.rewrite.model.factory.impl.AzBuiltinBakedModelFactory; +import mod.azure.azurelib.util.AzureLibUtil; + +import java.util.Map; + +/** + * A registry for managing instances of {@link AzBakedModelFactory} that are used to handle the creation of baked models + * for specific namespaces. This allows custom behavior for different mods or namespaces when constructing models.
+ * This class provides functionality to register, retrieve, and manage baked model factories. It ensures that a default + * factory is available for any namespace that does not explicitly register a custom factory. + */ +public class AzBakedModelFactoryRegistry { + + private static final Map FACTORIES = new Object2ObjectOpenHashMap<>(1); + + private static final AzBakedModelFactory DEFAULT_FACTORY = new AzBuiltinBakedModelFactory(); + + public static AzBakedModelFactory getForNamespace(String namespace) { + return FACTORIES.getOrDefault(namespace, DEFAULT_FACTORY); + } + + /** + * Register a custom {@link AzBakedModelFactory} to handle loading models in a custom way.
+ * MUST be called during mod construct
+ * It is recommended you don't call this directly, and instead call it via + * {@link AzureLibUtil#addCustomBakedModelFactory} + * + * @param namespace The namespace (modid) to register the factory for + * @param factory The factory responsible for model loading under the given namespace + */ + public static void register(String namespace, AzBakedModelFactory factory) { + FACTORIES.put(namespace, factory); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzLayerRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzLayerRenderer.java new file mode 100644 index 000000000..4a5e44490 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzLayerRenderer.java @@ -0,0 +1,52 @@ +package mod.azure.azurelib.rewrite.render; + +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; + +import java.util.Collection; +import java.util.function.Supplier; + +/** + * Provides a mechanism to manage and apply multiple render layers for a given animatable context. This class acts as a + * wrapper for handling collections of render layers and delegates the rendering responsibilities to the individual + * layers. + * + * @param The type of animatable entity or object the render layers apply to. + */ +public class AzLayerRenderer { + + private final Supplier>> renderLayerSupplier; + + public AzLayerRenderer(Supplier>> renderLayerSupplier) { + this.renderLayerSupplier = renderLayerSupplier; + } + + /** + * Calls back to the various {@link AzRenderLayer RenderLayers} that have been registered to this renderer for their + * {@link AzRenderLayer#preRender pre-render} actions. + */ + protected void preApplyRenderLayers(AzRendererPipelineContext context) { + for (var renderLayer : renderLayerSupplier.get()) { + renderLayer.preRender(context); + } + } + + /** + * Calls back to the various {@link AzRenderLayer RenderLayers} that have been registered to this renderer for their + * {@link AzRenderLayer#renderForBone per-bone} render actions. + */ + public void applyRenderLayersForBone(AzRendererPipelineContext context, AzBone bone) { + for (var renderLayer : renderLayerSupplier.get()) { + renderLayer.renderForBone(context, bone); + } + } + + /** + * Render the various {@link AzRenderLayer RenderLayers} that have been registered to this renderer + */ + protected void applyRenderLayers(AzRendererPipelineContext context) { + for (var renderLayer : renderLayerSupplier.get()) { + renderLayer.render(context); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzModelRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzModelRenderer.java new file mode 100644 index 000000000..92e01806c --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzModelRenderer.java @@ -0,0 +1,165 @@ +package mod.azure.azurelib.rewrite.render; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import mod.azure.azurelib.cache.object.GeoCube; +import mod.azure.azurelib.cache.object.GeoQuad; +import mod.azure.azurelib.cache.object.GeoVertex; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.util.RenderUtils; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; + +/** + * AzModelRenderer provides a generic and extensible base class for rendering models by processing hierarchical bone + * structures recursively. It leverages a rendering pipeline and a layer renderer to facilitate advanced rendering + * tasks, including layer application and animated texture processing. + * + * @param the type of animatable object this renderer supports + */ +public class AzModelRenderer { + + private final Matrix4f poseStateCache = new Matrix4f(); + + private final Vector3f normalCache = new Vector3f(); + + private final AzRendererPipeline rendererPipeline; + + protected final AzLayerRenderer layerRenderer; + + public AzModelRenderer(AzRendererPipeline rendererPipeline, AzLayerRenderer layerRenderer) { + this.layerRenderer = layerRenderer; + this.rendererPipeline = rendererPipeline; + } + + /** + * The actual render method that sub-type renderers should override to handle their specific rendering tasks.
+ */ + protected void render(AzRendererPipelineContext context, boolean isReRender) { + var animatable = context.animatable(); + var model = context.bakedModel(); + + rendererPipeline.updateAnimatedTextureFrame(animatable); + + for (var bone : model.getTopLevelBones()) { + renderRecursively(context, bone, isReRender); + } + } + + /** + * Renders the provided {@link AzBone} and its associated child bones + */ + protected void renderRecursively(AzRendererPipelineContext context, AzBone bone, boolean isReRender) { + var poseStack = context.poseStack(); + + poseStack.pushPose(); + RenderUtils.prepMatrixForBone(poseStack, bone); + renderCubesOfBone(context, bone); + + if (!isReRender) { + layerRenderer.applyRenderLayersForBone(context, bone); + } + + renderChildBones(context, bone, isReRender); + poseStack.popPose(); + } + + /** + * Renders the {@link GeoCube GeoCubes} associated with a given {@link AzBone} + */ + protected void renderCubesOfBone(AzRendererPipelineContext context, AzBone bone) { + if (bone.isHidden()) { + return; + } + + var poseStack = context.poseStack(); + + for (var cube : bone.getCubes()) { + poseStack.pushPose(); + + renderCube(context, cube); + + poseStack.popPose(); + } + } + + /** + * Render the child bones of a given {@link AzBone}.
+ * Note that this does not render the bone itself. That should be done through + * {@link AzModelRenderer#renderCubesOfBone} separately + */ + protected void renderChildBones(AzRendererPipelineContext context, AzBone bone, boolean isReRender) { + if (bone.isHidingChildren()) + return; + + for (var childBone : bone.getChildBones()) { + renderRecursively(context, childBone, isReRender); + } + } + + /** + * Renders an individual {@link GeoCube}.
+ * This tends to be called recursively from something like {@link AzModelRenderer#renderCubesOfBone} + */ + protected void renderCube(AzRendererPipelineContext context, GeoCube cube) { + var poseStack = context.poseStack(); + + RenderUtils.translateToPivotPoint(poseStack, cube); + RenderUtils.rotateMatrixAroundCube(poseStack, cube); + RenderUtils.translateAwayFromPivotPoint(poseStack, cube); + + var normalisedPoseState = poseStack.last().normal(); + var poseState = poseStateCache.set(poseStack.last().pose()); + + for (var quad : cube.quads()) { + if (quad == null) { + continue; + } + + normalCache.set(quad.normal()); + var normal = normalisedPoseState.transform(normalCache); + + RenderUtils.fixInvertedFlatCube(cube, normal); + createVerticesOfQuad(context, quad, poseState, normal); + } + } + + private final Vector4f poseStateTransformCache = new Vector4f(); + + /** + * Applies the {@link GeoQuad Quad's} {@link GeoVertex vertices} to the given {@link VertexConsumer buffer} for + * rendering + */ + protected void createVerticesOfQuad( + AzRendererPipelineContext context, + GeoQuad quad, + Matrix4f poseState, + Vector3f normal + ) { + var buffer = context.vertexConsumer(); + var packedOverlay = context.packedOverlay(); + var packedLight = context.packedLight(); + + for (var vertex : quad.vertices()) { + var position = vertex.position(); + poseStateTransformCache.set(position.x(), position.y(), position.z(), 1.0f); + var vector4f = poseState.transform(poseStateTransformCache); + + buffer.vertex( + vector4f.x(), + vector4f.y(), + vector4f.z(), + context.red(), + context.green(), + context.blue(), + context.alpha(), + vertex.texU(), + vertex.texV(), + packedOverlay, + packedLight, + normal.x(), + normal.y(), + normal.z()); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzPhasedRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzPhasedRenderer.java new file mode 100644 index 000000000..c295c1812 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzPhasedRenderer.java @@ -0,0 +1,29 @@ +package mod.azure.azurelib.rewrite.render; + +import com.mojang.blaze3d.vertex.PoseStack; + +/** + * Represents a phased renderer interface used as part of a rendering pipeline. Allows customization at specific stages + * of the rendering process for animatable models.
+ * The interface provides two key methods, enabling actions to be taken before and after the core rendering operations + * within the pipeline, specifically focusing on transforming and modifying render contexts.
+ * This is part of a flexible rendering system that enables complex rendering logic while maintaining separation of + * concerns and modularity. + * + * @param The type of animatable object being rendered. + */ +public interface AzPhasedRenderer { + + /** + * Called before rendering the model to buffer. Allows for render modifications and preparatory work such as scaling + * and translating.
+ * {@link PoseStack} translations made here are kept until the end of the render process + */ + void preRender(AzRendererPipelineContext context, boolean isReRender); + + /** + * Called after rendering the model to buffer. Post-render modifications should be performed here.
+ * {@link PoseStack} transformations will be unused and lost once this method ends + */ + void postRender(AzRendererPipelineContext context, boolean isReRender); +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzProvider.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzProvider.java new file mode 100644 index 000000000..4d4a7c85a --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzProvider.java @@ -0,0 +1,78 @@ +package mod.azure.azurelib.rewrite.render; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.model.cache.AzBakedModelCache; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * The {@code AzProvider} class serves as a utility for providing animation-related resources, such as baked models and + * animators for animatable objects of type {@code T}. This class facilitates the dynamic retrieval and caching of + * resources to enhance performance during runtime and minimize redundant resource generation. + * + * @param The type of the animatable object this provider works with (e.g., an entity, block, or item). + */ +public class AzProvider { + + private final Supplier> animatorSupplier; + + private final Function modelLocationProvider; + + public AzProvider( + Supplier> animatorSupplier, + Function modelLocationProvider + ) { + this.animatorSupplier = animatorSupplier; + this.modelLocationProvider = modelLocationProvider; + } + + /** + * Provides a baked model associated with the specified animatable object. This method retrieves the model resource + * location for the animatable object using the configured model location provider, then fetches the corresponding + * baked model from the {@link AzBakedModelCache}. + * + * @param animatable the animatable object for which the baked model should be retrieved, must not be null + * @return the baked model associated with the animatable object, or null if no model is found + */ + public @Nullable AzBakedModel provideBakedModel(@NotNull T animatable) { + var modelResourceLocation = modelLocationProvider.apply(animatable); + return AzBakedModelCache.getInstance().getNullable(modelResourceLocation); + } + + /** + * Provides an {@link AzAnimator} instance associated with the given animatable object. If the animator is not + * already cached, this method will create a new animator, register its controllers, and cache it for future use. + * + * @param animatable the animatable object for which the animator should be provided + * @return an {@link AzAnimator} instance associated with the animatable object, or null if the animator could not + * be created or retrieved + */ + public @Nullable AzAnimator provideAnimator(T animatable) { + // TODO: Instead of caching the entire animator itself, we're going to want to cache the relevant data for the + // entity. + var accessor = AzAnimatorAccessor.cast(animatable); + var cachedAnimator = accessor.getAnimatorOrNull(); + + if (cachedAnimator == null) { + // If the cached animator is null, create a new one. We use a separate reference here just for some + cachedAnimator = animatorSupplier.get(); + + if (cachedAnimator != null) { + // If the new animator we created is not null, then register its controllers. + cachedAnimator.registerControllers( + cachedAnimator.getAnimationControllerContainer() + ); + // Also cache the animator so that the next time we fetch the animator, it's ready for us. + accessor.setAnimator(cachedAnimator); + } + } + + return cachedAnimator; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java new file mode 100644 index 000000000..85efd86de --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java @@ -0,0 +1,186 @@ +package mod.azure.azurelib.rewrite.render; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * The {@code AzRendererConfig} class is a configuration class used for defining rendering configurations for generic + * animatable objects. It allows customization of model and texture locations, animators, render layers, and scale + * factors. + * + * @param The type of animatable object this configuration applies to. + */ +public class AzRendererConfig { + + private final Supplier<@Nullable AzAnimator> animatorProvider; + + private final Function modelLocationProvider; + + private final Function renderTypeFunction; + + private final List> renderLayers; + + private final Function textureLocationProvider; + + private final float scaleHeight; + + private final float scaleWidth; + + public AzRendererConfig( + Supplier> animatorProvider, + Function modelLocationProvider, + Function renderTypeFunction, + List> renderLayers, + Function textureLocationProvider, + float scaleHeight, + float scaleWidth + ) { + this.animatorProvider = animatorProvider; + this.modelLocationProvider = modelLocationProvider; + this.renderTypeFunction = renderTypeFunction; + this.renderLayers = Collections.unmodifiableList(renderLayers); + this.textureLocationProvider = textureLocationProvider; + this.scaleHeight = scaleHeight; + this.scaleWidth = scaleWidth; + } + + public @Nullable AzAnimator createAnimator() { + return animatorProvider.get(); + } + + public ResourceLocation modelLocation(T animatable) { + return modelLocationProvider.apply(animatable); + } + + public ResourceLocation textureLocation(T animatable) { + return textureLocationProvider.apply(animatable); + } + + public RenderType getRenderType(T entity) { + return renderTypeFunction.apply(entity); + } + + public List> renderLayers() { + return renderLayers; + } + + public float scaleHeight() { + return scaleHeight; + } + + public float scaleWidth() { + return scaleWidth; + } + + public static class Builder { + + private final Function modelLocationProvider; + + protected Function renderTypeProvider; + + private final List> renderLayers; + + private final Function textureLocationProvider; + + private Supplier<@Nullable AzAnimator> animatorProvider; + + private float scaleHeight; + + private float scaleWidth; + + protected Builder( + Function modelLocationProvider, + Function textureLocationProvider + ) { + this.animatorProvider = () -> null; + this.modelLocationProvider = modelLocationProvider; + this.renderTypeProvider = $ -> RenderType.entityCutoutNoCull(textureLocationProvider.apply($)); + this.renderLayers = new ObjectArrayList<>(); + this.textureLocationProvider = textureLocationProvider; + this.scaleHeight = 1; + this.scaleWidth = 1; + } + + /** + * Sets the animator provider for the builder. The animator provider is responsible for supplying an instance of + * {@link AzAnimator} that defines the animation logic for the target object. + * + * @param animatorProvider a {@link Supplier} that provides a {@link AzAnimator} instance or null if no custom + * animation logic is required + * @return the updated {@code Builder} instance for chaining configuration methods + */ + public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animatorProvider) { + this.animatorProvider = animatorProvider; + return this; + } + + /** + * Adds a {@link AzRenderLayer} to this config, to be called after the main model is rendered each frame + */ + public Builder addRenderLayer(AzRenderLayer renderLayer) { + this.renderLayers.add(renderLayer); + return this; + } + + public Builder setRenderType(RenderType renderType) { + this.renderTypeProvider = $ -> renderType; + return this; + } + + public Builder setRenderType(Function renderTypeProvider) { + this.renderTypeProvider = renderTypeProvider; + return this; + } + + /** + * Sets the scaling factor uniformly for both width and height dimensions. + * + * @param scale the uniform scaling factor to be applied to both width and height + * @return the {@code Builder} instance for method chaining + */ + public Builder setScale(float scale) { + return setScale(scale, scale); + } + + /** + * Sets the scaling factors for both width and height. + * + * @param scaleWidth the scaling factor for the width + * @param scaleHeight the scaling factor for the height + * @return the updated builder instance for chaining operations + */ + public Builder setScale(float scaleWidth, float scaleHeight) { + this.scaleHeight = scaleHeight; + this.scaleWidth = scaleWidth; + return this; + } + + /** + * Builds and returns a finalized {@link AzRendererConfig} instance with the current configuration settings + * provided through the builder. + * + * @return a new instance of {@link AzRendererConfig} configured with the specified animator provider, model + * location provider, texture location provider, render layers, and scale factors. + */ + public AzRendererConfig build() { + return new AzRendererConfig<>( + animatorProvider, + modelLocationProvider, + renderTypeProvider, + renderLayers, + textureLocationProvider, + scaleHeight, + scaleWidth + ); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipeline.java new file mode 100644 index 000000000..afcc0bc47 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipeline.java @@ -0,0 +1,189 @@ +package mod.azure.azurelib.rewrite.render; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import mod.azure.azurelib.cache.texture.AnimatableTexture; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import org.jetbrains.annotations.Nullable; + +/** + * Abstract base class for defining a rendering pipeline. The {@code AzRendererPipeline} provides a structured framework + * to handle complex rendering tasks by separating responsibilities into different components, such as layer rendering + * and model rendering. + * + * @param The type of the object to be rendered. + */ +public abstract class AzRendererPipeline implements AzPhasedRenderer { + + protected final AzRendererConfig config; + + private final AzRendererPipelineContext context; + + private final AzLayerRenderer layerRenderer; + + private final AzModelRenderer modelRenderer; + + protected AzRendererPipeline(AzRendererConfig config) { + this.config = config; + this.context = createContext(this); + this.layerRenderer = createLayerRenderer(config); + this.modelRenderer = createModelRenderer(layerRenderer); + } + + /** + * Creates a rendering pipeline context for the specified renderer pipeline. This method is intended to be + * implemented by subclasses to provide a specific implementation of the {@link AzRendererPipelineContext} for + * rendering. + * + * @param rendererPipeline the renderer pipeline for which the context is to be created + * @return a new instance of {@link AzRendererPipelineContext} specific to the given renderer pipeline + */ + protected abstract AzRendererPipelineContext createContext(AzRendererPipeline rendererPipeline); + + /** + * Creates an instance of {@link AzModelRenderer} using the provided {@link AzLayerRenderer}. This method is part of + * the rendering pipeline and is responsible for generating a model renderer which can handle hierarchical + * structures and advanced rendering tasks. + * + * @param layerRenderer the {@link AzLayerRenderer} instance used to decorate and handle additional render layers + * within the model rendering process + * @return a new instance of {@link AzModelRenderer} configured with the provided layer renderer + */ + protected abstract AzModelRenderer createModelRenderer(AzLayerRenderer layerRenderer); + + /** + * Creates an instance of {@link AzLayerRenderer} using the provided {@link AzRendererConfig}. This method is + * responsible for generating a layer renderer configured with the provided rendering configuration, allowing for + * the management and application of multiple render layers. + * + * @param config The configuration object of type {@link AzRendererConfig} that provides the necessary settings and + * parameters for the layer renderer. + * @return A newly created {@link AzLayerRenderer} instance configured based on the specified + * {@link AzRendererConfig}. + */ + protected abstract AzLayerRenderer createLayerRenderer(AzRendererConfig config); + + /** + * Update the current frame of a {@link AnimatableTexture potentially animated} texture used by this + * GeoRenderer.
+ * This should only be called immediately prior to rendering, and only + * + * @see AnimatableTexture#setAndUpdate + */ + protected abstract void updateAnimatedTextureFrame(T animatable); + + /** + * Initial access point for rendering. It all begins here.
+ * All AzureLib renderers should immediately defer their respective default {@code render} calls to this, for + * consistent handling + */ + public void render( + PoseStack poseStack, + AzBakedModel model, + T animatable, + MultiBufferSource bufferSource, + @Nullable RenderType renderType, + @Nullable VertexConsumer buffer, + float yaw, + float partialTick, + int packedLight, + float red, + float green, + float blue, + float alpha + ) { + renderType = config.getRenderType(animatable); + context.populate(animatable, model, bufferSource, packedLight, partialTick, poseStack, renderType, buffer, red, blue, green, alpha); + + poseStack.pushPose(); + + preRender(context, false); + + // TODO: + // if (firePreRenderEvent(poseStack, model, bufferSource, partialTick, packedLight)) { + layerRenderer.preApplyRenderLayers(context); + modelRenderer.render(context, false); + layerRenderer.applyRenderLayers(context); + postRender(context, false); + // TODO: + // firePostRenderEvent(poseStack, model, bufferSource, partialTick, packedLight); + // } + + poseStack.popPose(); + + renderFinal(context); + doPostRenderCleanup(); + } + + /** + * Re-renders the provided {@link AzBakedModel}.
+ * Usually you'd use this for rendering alternate {@link RenderType} layers or for sub-model rendering whilst inside + * a {@link AzRenderLayer} or similar + */ + public void reRender(AzRendererPipelineContext context) { + var poseStack = context.poseStack(); + + poseStack.pushPose(); + + preRender(context, true); + modelRenderer.render(context, true); + postRender(context, true); + + poseStack.popPose(); + } + + /** + * Call after all other rendering work has taken place, including reverting the {@link PoseStack}'s state. This + * method is not called in {@link AzRendererPipeline#reRender re-render} + */ + protected void renderFinal(AzRendererPipelineContext context) {} + + /** + * Called after all render operations are completed and the render pass is considered functionally complete. + *

+ * Use this method to clean up any leftover persistent objects stored during rendering or any other post-render + * maintenance tasks as required + */ + protected void doPostRenderCleanup() {} + + /** + * Scales the {@link PoseStack} in preparation for rendering the model, excluding when re-rendering the model as + * part of a {@link AzRenderLayer} or external render call.
+ * Override and call super with modified scale values as needed to further modify the scale of the model (E.G. child + * entities) + */ + protected void scaleModelForRender( + AzRendererPipelineContext context, + float widthScale, + float heightScale, + boolean isReRender + ) { + if (!isReRender && (widthScale != 1 || heightScale != 1)) { + var poseStack = context.poseStack(); + poseStack.scale(widthScale, heightScale, widthScale); + } + } + + /** + * Provides access to the rendering configuration associated with this rendering pipeline. + * + * @return An instance of {@link AzRendererConfig} that contains the configuration details for this rendering + * pipeline, including animator, model location, texture location, render layers, and scaling parameters. + */ + public AzRendererConfig config() { + return config; + } + + /** + * Provides access to the rendering pipeline context associated with this rendering pipeline. + * + * @return An instance of {@link AzRendererPipelineContext} representing the context for the current rendering + * pipeline, containing relevant rendering data and configurations for processing animations and models. + */ + public AzRendererPipelineContext context() { + return context; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java new file mode 100644 index 000000000..0d9fde789 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java @@ -0,0 +1,204 @@ +package mod.azure.azurelib.rewrite.render; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +/** + * An abstract base class representing the rendering context for a custom rendering pipeline. This class provides + * generic rendering properties and behavior that can be extended to customize rendering for different types of + * animatable objects. + * + * @param the type of the animatable object being rendered + */ +public abstract class AzRendererPipelineContext { + + private final AzRendererPipeline rendererPipeline; + + private T animatable; + + private AzBakedModel bakedModel; + + private MultiBufferSource multiBufferSource; + + private int packedLight; + + private int packedOverlay; + + private float partialTick; + + private PoseStack poseStack; + + private float red; + + private float green; + + private float blue; + + private float alpha; + + private @Nullable RenderType renderType; + + private VertexConsumer vertexConsumer; + + protected AzRendererPipelineContext(AzRendererPipeline rendererPipeline) { + this.rendererPipeline = rendererPipeline; + } + + /** + * Populates the rendering context with all necessary parameters required to render a specific animatable object. + * This method initializes the rendering pipeline with data such as the model, buffer source, lighting, and other + * associated properties for rendering the specified animatable object. + * + * @param animatable The animatable object that is being rendered. + * @param bakedModel The pre-baked 3D model associated with the animatable object. + * @param multiBufferSource The multibuffer source used for rendering vertex data. + * @param packedLight The packed light value for controlling light effects during rendering. + * @param partialTick The partial tick value for interpolating animations or movements. + * @param poseStack The pose stack used to manage rendering transformations. + * @param renderType The render type that determines how the object will be rendered, e.g., opaque, + * translucent, etc. + * @param vertexConsumer The vertex consumer used for buffering vertex attributes during rendering. + */ + public void populate( + T animatable, + AzBakedModel bakedModel, + MultiBufferSource multiBufferSource, + int packedLight, + float partialTick, + PoseStack poseStack, + RenderType renderType, + VertexConsumer vertexConsumer, + float red, + float green, + float blue, + float alpha + ) { + this.animatable = animatable; + this.bakedModel = bakedModel; + this.multiBufferSource = multiBufferSource; + this.packedLight = packedLight; + this.packedOverlay = getPackedOverlay(animatable, 0, partialTick); + this.partialTick = partialTick; + this.poseStack = poseStack; + this.renderType = renderType; + this.vertexConsumer = vertexConsumer; + this.red = red; + this.green = green; + this.blue = blue; + this.alpha = alpha; + + if (renderType == null) { + var textureLocation = rendererPipeline.config().textureLocation(animatable); + this.renderType = getDefaultRenderType(animatable, textureLocation, multiBufferSource, partialTick); + } + + Objects.requireNonNull(this.renderType); + + if (vertexConsumer == null) { + this.vertexConsumer = multiBufferSource.getBuffer(this.renderType); + } + } + + /** + * Gets the {@link RenderType} to render the given animatable with.
+ * Uses the {@link RenderType#entityCutoutNoCull} {@code RenderType} by default.
+ * Override this to change the way a model will render (such as translucent models, etc) + */ + public abstract @NotNull RenderType getDefaultRenderType( + T animatable, + ResourceLocation texture, + @Nullable MultiBufferSource bufferSource, + float partialTick + ); + + /** + * Gets a packed overlay coordinate pair for rendering.
+ * Mostly just used for the red tint when an entity is hurt, but can be used for other things like the + * {@link net.minecraft.world.entity.monster.Creeper} white tint when exploding. + */ + protected int getPackedOverlay(T animatable, float u, float partialTick) { + return OverlayTexture.NO_OVERLAY; + } + + public AzRendererPipeline rendererPipeline() { + return rendererPipeline; + } + + public T animatable() { + return animatable; + } + + public AzBakedModel bakedModel() { + return bakedModel; + } + + public MultiBufferSource multiBufferSource() { + return multiBufferSource; + } + + public float red() { + return red; + } + + public float green() { + return green; + } + + public float blue() { + return blue; + } + + public float alpha() { + return alpha; + } + + public int packedLight() { + return packedLight; + } + + public void setPackedLight(int packedLight) { + this.packedLight = packedLight; + } + + public int packedOverlay() { + return packedOverlay; + } + + public void setPackedOverlay(int packedOverlay) { + this.packedOverlay = packedOverlay; + } + + public float partialTick() { + return partialTick; + } + + public PoseStack poseStack() { + return poseStack; + } + + public @Nullable RenderType renderType() { + return renderType; + } + + public void setRenderType(@Nullable RenderType renderType) { + this.renderType = renderType; + } + + public VertexConsumer vertexConsumer() { + return vertexConsumer; + } + + public void setVertexConsumer(VertexConsumer vertexConsumer) { + this.vertexConsumer = vertexConsumer; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModel.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModel.java new file mode 100644 index 000000000..d2420e9a2 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModel.java @@ -0,0 +1,77 @@ +package mod.azure.azurelib.rewrite.render.armor; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.world.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class AzArmorModel extends HumanoidModel { + + private final AzArmorRendererPipeline rendererPipeline; + + public AzArmorModel(AzArmorRendererPipeline rendererPipeline) { + super(Minecraft.getInstance().getEntityModels().bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)); + this.rendererPipeline = rendererPipeline; + } + + @Override + public void renderToBuffer( + @NotNull PoseStack poseStack, + @Nullable VertexConsumer buffer, + int packedLight, + int packedOverlay, + float red, + float green, + float blue, + float alpha + ) { + var mc = Minecraft.getInstance(); + var context = rendererPipeline.context(); + var currentEntity = context.currentEntity(); + var currentStack = context.currentStack(); + MultiBufferSource bufferSource = Minecraft.getInstance().levelRenderer.renderBuffers.bufferSource(); + + var shouldOutline = Minecraft.getInstance().levelRenderer.shouldShowEntityOutlines() && mc + .shouldEntityAppearGlowing( + currentEntity + ); + + if (shouldOutline) { + bufferSource = Minecraft.getInstance().levelRenderer.renderBuffers.outlineBufferSource(); + } + + var config = rendererPipeline.config(); + var animatable = context.animatable(); + var partialTick = mc.getFrameTime(); + var textureLocation = config.textureLocation(animatable); + var renderType = context.getDefaultRenderType(animatable, textureLocation, bufferSource, partialTick); + buffer = ItemRenderer.getArmorFoilBuffer(bufferSource, renderType, false, currentStack.hasFoil()); + + var model = rendererPipeline.renderer().provider().provideBakedModel(animatable); + rendererPipeline.render(poseStack, model, animatable, bufferSource, null, buffer, 0, partialTick, packedLight, red, green, blue, alpha); + } + + /** + * Applies settings and transformations pre-render based on the default model + */ + public void applyBaseModel(HumanoidModel baseModel) { + this.young = baseModel.young; + this.crouching = baseModel.crouching; + this.riding = baseModel.riding; + this.rightArmPose = baseModel.rightArmPose; + this.leftArmPose = baseModel.leftArmPose; + } + + @Override + public void setAllVisible(boolean pVisible) { + super.setAllVisible(pVisible); + var boneContext = rendererPipeline.context().boneContext(); + boneContext.setAllVisible(pVisible); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModelRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModelRenderer.java new file mode 100644 index 000000000..ab30de465 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModelRenderer.java @@ -0,0 +1,79 @@ +package mod.azure.azurelib.rewrite.render.armor; + +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.AzLayerRenderer; +import mod.azure.azurelib.rewrite.render.AzModelRenderer; +import mod.azure.azurelib.rewrite.render.AzPhasedRenderer; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.world.item.ItemStack; +import org.joml.Matrix4f; +import org.joml.Vector3f; + +public class AzArmorModelRenderer extends AzModelRenderer { + + private final AzArmorRendererPipeline armorRendererPipeline; + + public AzArmorModelRenderer( + AzArmorRendererPipeline armorRendererPipeline, + AzLayerRenderer layerRenderer + ) { + super(armorRendererPipeline, layerRenderer); + this.armorRendererPipeline = armorRendererPipeline; + } + + /** + * The actual render method that subtype renderers should override to handle their specific rendering tasks.
+ * {@link AzPhasedRenderer#preRender} has already been called by this stage, and {@link AzPhasedRenderer#postRender} + * will be called directly after + */ + @Override + public void render(AzRendererPipelineContext context, boolean isReRender) { + var poseStack = context.poseStack(); + poseStack.pushPose(); + poseStack.translate(0, 24 / 16f, 0); + poseStack.scale(-1, -1, 1); + + if (!isReRender) { + var animatable = context.animatable(); + var animator = armorRendererPipeline.renderer().animator(); + + if (animator != null) { + animator.animate(animatable, context.partialTick()); + } + } + + armorRendererPipeline.modelRenderTranslations = new Matrix4f(poseStack.last().pose()); + + super.render(context, isReRender); + poseStack.popPose(); + } + + /** + * Renders the provided {@link AzBone} and its associated child bones + */ + @Override + public void renderRecursively(AzRendererPipelineContext context, AzBone bone, boolean isReRender) { + var poseStack = context.poseStack(); + // TODO: This is dangerous. + var ctx = armorRendererPipeline.context(); + + if (bone.isTrackingMatrices()) { + Matrix4f poseState = new Matrix4f(poseStack.last().pose()); + Matrix4f localMatrix = RenderUtils.invertAndMultiplyMatrices( + poseState, + armorRendererPipeline.entityRenderTranslations + ); + + bone.setModelSpaceMatrix( + RenderUtils.invertAndMultiplyMatrices(poseState, armorRendererPipeline.modelRenderTranslations) + ); + bone.setLocalSpaceMatrix(RenderUtils.translateMatrix(localMatrix, new Vector3f())); + bone.setWorldSpaceMatrix( + RenderUtils.translateMatrix(new Matrix4f(localMatrix), ctx.currentEntity().position().toVector3f()) + ); + } + + super.renderRecursively(context, bone, isReRender); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRenderer.java new file mode 100644 index 000000000..5c7c0fcac --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRenderer.java @@ -0,0 +1,80 @@ +package mod.azure.azurelib.rewrite.render.armor; + +import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.render.AzProvider; +import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +public class AzArmorRenderer { + + private final AzProvider provider; + + private final AzArmorRendererPipeline rendererPipeline; + + @Nullable + private AzItemAnimator reusedAzItemAnimator; + + public AzArmorRenderer(AzRendererConfig config) { + this.provider = new AzProvider<>(config::createAnimator, config::modelLocation); + this.rendererPipeline = createPipeline(config); + } + + protected AzArmorRendererPipeline createPipeline(AzRendererConfig config) { + return new AzArmorRendererPipeline(config, this); + } + + /** + * Prepare the renderer for the current render cycle.
+ * Must be called prior to render as the default HumanoidModel doesn't give render context.
+ * Params have been left nullable so that the renderer can be called for model/texture purposes safely. If you do + * grab the renderer using null parameters, you should not use it for actual rendering. + * + * @param entity The entity being rendered with the armor on + * @param stack The ItemStack being rendered + * @param slot The slot being rendered + * @param baseModel The default (vanilla) model that would have been rendered if this model hadn't replaced it + */ + public void prepForRender( + @Nullable Entity entity, + ItemStack stack, + @Nullable EquipmentSlot slot, + @Nullable HumanoidModel baseModel + ) { + if (entity == null || slot == null || baseModel == null) { + return; + } + + rendererPipeline.context().prepare(entity, stack, slot, baseModel); + + var model = provider.provideBakedModel(stack); + prepareAnimator(stack, model); + } + + private void prepareAnimator(ItemStack stack, AzBakedModel model) { + var cachedEntityAnimator = (AzItemAnimator) provider.provideAnimator(stack); + + if (cachedEntityAnimator != null && model != null) { + cachedEntityAnimator.setActiveModel(model); + } + + // Point the renderer's current animator reference to the cached entity animator before rendering. + reusedAzItemAnimator = cachedEntityAnimator; + } + + public @Nullable AzItemAnimator animator() { + return reusedAzItemAnimator; + } + + public AzProvider provider() { + return provider; + } + + public AzArmorRendererPipeline rendererPipeline() { + return rendererPipeline; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java new file mode 100644 index 000000000..c9ad35be2 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java @@ -0,0 +1,105 @@ +package mod.azure.azurelib.rewrite.render.armor; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import mod.azure.azurelib.rewrite.render.armor.bone.AzArmorBoneProvider; +import mod.azure.azurelib.rewrite.render.armor.bone.AzDefaultArmorBoneProvider; +import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +public class AzArmorRendererConfig extends AzRendererConfig { + + private final AzArmorBoneProvider boneProvider; + + private AzArmorRendererConfig( + Supplier> animatorProvider, + AzArmorBoneProvider boneProvider, + Function modelLocationProvider, + Function renderTypeProvider, + List> renderLayers, + Function textureLocationProvider, + float scaleHeight, + float scaleWidth + ) { + super( + animatorProvider, + modelLocationProvider, + renderTypeProvider, + renderLayers, + textureLocationProvider, + scaleHeight, + scaleWidth + ); + this.boneProvider = boneProvider; + } + + public AzArmorBoneProvider boneProvider() { + return boneProvider; + } + + public static Builder builder( + ResourceLocation modelLocation, + ResourceLocation textureLocation + ) { + return new Builder($ -> modelLocation, $ -> textureLocation); + } + + public static Builder builder( + Function modelLocationProvider, + Function textureLocationProvider + ) { + return new Builder(modelLocationProvider, textureLocationProvider); + } + + public static class Builder extends AzRendererConfig.Builder { + + private AzArmorBoneProvider boneProvider; + + protected Builder( + Function modelLocationProvider, + Function textureLocationProvider + ) { + super(modelLocationProvider, textureLocationProvider); + this.boneProvider = new AzDefaultArmorBoneProvider(); + this.renderTypeProvider = $ -> RenderType.armorCutoutNoCull(textureLocationProvider.apply($)); + } + + @Override + public Builder addRenderLayer(AzRenderLayer renderLayer) { + return (Builder) super.addRenderLayer(renderLayer); + } + + @Override + public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animatorProvider) { + return (Builder) super.setAnimatorProvider(animatorProvider); + } + + public Builder setBoneProvider(AzArmorBoneProvider boneProvider) { + this.boneProvider = boneProvider; + return this; + } + + @Override + public AzArmorRendererConfig build() { + var baseConfig = super.build(); + + return new AzArmorRendererConfig( + baseConfig::createAnimator, + boneProvider, + baseConfig::modelLocation, + baseConfig::getRenderType, + baseConfig.renderLayers(), + baseConfig::textureLocation, + baseConfig.scaleHeight(), + baseConfig.scaleWidth() + ); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java new file mode 100644 index 000000000..9c942c7b5 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java @@ -0,0 +1,126 @@ +package mod.azure.azurelib.rewrite.render.armor; + +import mod.azure.azurelib.cache.texture.AnimatableTexture; +import mod.azure.azurelib.rewrite.render.*; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import org.joml.Matrix4f; + +public class AzArmorRendererPipeline extends AzRendererPipeline { + + private final AzArmorModel armorModel; + + private final AzArmorRenderer armorRenderer; + + protected Matrix4f entityRenderTranslations = new Matrix4f(); + + protected Matrix4f modelRenderTranslations = new Matrix4f(); + + public AzArmorRendererPipeline(AzRendererConfig config, AzArmorRenderer armorRenderer) { + super(config); + this.armorModel = new AzArmorModel<>(this); + this.armorRenderer = armorRenderer; + } + + @Override + protected AzRendererPipelineContext createContext(AzRendererPipeline rendererPipeline) { + return new AzArmorRendererPipelineContext(rendererPipeline); + } + + @Override + protected AzModelRenderer createModelRenderer(AzLayerRenderer layerRenderer) { + return new AzArmorModelRenderer(this, layerRenderer); + } + + @Override + protected AzLayerRenderer createLayerRenderer(AzRendererConfig config) { + return new AzLayerRenderer<>(config::renderLayers); + } + + @Override + protected void updateAnimatedTextureFrame(ItemStack animatable) { + var currentEntity = context().currentEntity(); + + if (currentEntity != null) { + var textureLocation = config().textureLocation(animatable); + var frameTick = currentEntity.getId() + currentEntity.tickCount; + + AnimatableTexture.setAndUpdate(textureLocation, frameTick); + } + } + + @Override + public void preRender(AzRendererPipelineContext context, boolean isReRender) { + var armorContext = (AzArmorRendererPipelineContext) context; + var baseModel = armorContext.baseModel(); + var boneContext = armorContext.boneContext(); + var config = config(); + var currentSlot = armorContext.currentSlot(); + var scaleWidth = config.scaleWidth(); + var scaleHeight = config.scaleHeight(); + + var animatable = armorContext.animatable(); + var model = armorRenderer.provider().provideBakedModel(animatable); + var poseStack = armorContext.poseStack(); + + this.entityRenderTranslations = new Matrix4f(poseStack.last().pose()); + + armorModel.applyBaseModel(baseModel); + boneContext.grabRelevantBones(model, config.boneProvider()); + boneContext.applyBaseTransformations(baseModel); + scaleModelForBaby(armorContext, isReRender); + scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); + + boneContext.applyBoneVisibilityBySlot(currentSlot); + } + + @Override + public void postRender(AzRendererPipelineContext context, boolean isReRender) {} + + /** + * Apply custom scaling to account for {@link net.minecraft.client.model.AgeableListModel AgeableListModel} baby + * models + */ + public void scaleModelForBaby(AzArmorRendererPipelineContext context, boolean isReRender) { + if (!armorModel.young || isReRender) { + return; + } + + var baseModel = context.baseModel(); + var currentSlot = context.currentSlot(); + var poseStack = context.poseStack(); + + if (currentSlot == EquipmentSlot.HEAD) { + if (baseModel.scaleHead) { + float headScale = 1.5f / baseModel.babyHeadScale; + + poseStack.scale(headScale, headScale, headScale); + } + + poseStack.translate(0, baseModel.babyYHeadOffset / 16f, baseModel.babyZHeadOffset / 16f); + } else { + float bodyScale = 1 / baseModel.babyBodyScale; + + poseStack.scale(bodyScale, bodyScale, bodyScale); + poseStack.translate(0, baseModel.bodyYOffset / 16f, 0); + } + } + + public AzArmorModel armorModel() { + return armorModel; + } + + @Override + public AzArmorRendererConfig config() { + return (AzArmorRendererConfig) super.config(); + } + + @Override + public AzArmorRendererPipelineContext context() { + return (AzArmorRendererPipelineContext) super.context(); + } + + public AzArmorRenderer renderer() { + return armorRenderer; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipelineContext.java new file mode 100644 index 000000000..e6fa0dbb6 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipelineContext.java @@ -0,0 +1,78 @@ +package mod.azure.azurelib.rewrite.render.armor; + +import mod.azure.azurelib.rewrite.render.AzRendererPipeline; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.rewrite.render.armor.bone.AzArmorBoneContext; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class AzArmorRendererPipelineContext extends AzRendererPipelineContext { + + private final AzArmorBoneContext boneContext; + + private HumanoidModel baseModel; + + private Entity currentEntity; + + private EquipmentSlot currentSlot; + + private ItemStack currentStack; + + public AzArmorRendererPipelineContext(AzRendererPipeline rendererPipeline) { + super(rendererPipeline); + this.baseModel = null; + this.boneContext = new AzArmorBoneContext(); + this.currentEntity = null; + this.currentSlot = null; + this.currentStack = null; + } + + @Override + public @NotNull RenderType getDefaultRenderType( + ItemStack animatable, + ResourceLocation texture, + @Nullable MultiBufferSource bufferSource, + float partialTick + ) { + return RenderType.armorCutoutNoCull(texture); + } + + public void prepare( + @Nullable Entity entity, + ItemStack stack, + @Nullable EquipmentSlot slot, + @Nullable HumanoidModel baseModel + ) { + this.baseModel = baseModel; + this.currentEntity = entity; + this.currentStack = stack; + this.currentSlot = slot; + } + + public HumanoidModel baseModel() { + return baseModel; + } + + public AzArmorBoneContext boneContext() { + return boneContext; + } + + public Entity currentEntity() { + return currentEntity; + } + + public EquipmentSlot currentSlot() { + return currentSlot; + } + + public ItemStack currentStack() { + return currentStack; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererRegistry.java new file mode 100644 index 000000000..631b01b46 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererRegistry.java @@ -0,0 +1,34 @@ +package mod.azure.azurelib.rewrite.render.armor; + +import net.minecraft.world.item.Item; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public class AzArmorRendererRegistry { + + private static final Map ITEM_TO_RENDERER = new HashMap<>(); + + private static final Map> ITEM_TO_RENDERER_SUPPLIER = new HashMap<>(); + + public static void register(Item item, Supplier armorRendererSupplier) { + ITEM_TO_RENDERER_SUPPLIER.put(item, armorRendererSupplier); + } + + public static void register(Supplier armorRendererSupplier, Item item, Item... items) { + register(item, armorRendererSupplier); + + for (var otherItem : items) { + register(otherItem, armorRendererSupplier); + } + } + + public static @Nullable AzArmorRenderer getOrNull(Item item) { + return ITEM_TO_RENDERER.computeIfAbsent(item, ($) -> { + var rendererSupplier = ITEM_TO_RENDERER_SUPPLIER.get(item); + return rendererSupplier == null ? null : rendererSupplier.get(); + }); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneContext.java new file mode 100644 index 000000000..3f127eb85 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneContext.java @@ -0,0 +1,197 @@ +package mod.azure.azurelib.rewrite.render.armor.bone; + +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.world.entity.EquipmentSlot; +import org.jetbrains.annotations.Nullable; + +public class AzArmorBoneContext { + + private AzBakedModel lastModel; + + protected AzBone head; + + protected AzBone body; + + protected AzBone rightArm; + + protected AzBone leftArm; + + protected AzBone rightLeg; + + protected AzBone leftLeg; + + protected AzBone rightBoot; + + protected AzBone leftBoot; + + public AzArmorBoneContext() { + this.head = null; + this.body = null; + this.rightArm = null; + this.leftArm = null; + this.rightLeg = null; + this.leftLeg = null; + this.rightBoot = null; + this.leftBoot = null; + } + + public void setAllVisible(boolean pVisible) { + setBoneVisible(this.head, pVisible); + setBoneVisible(this.body, pVisible); + setBoneVisible(this.rightArm, pVisible); + setBoneVisible(this.leftArm, pVisible); + setBoneVisible(this.rightLeg, pVisible); + setBoneVisible(this.leftLeg, pVisible); + setBoneVisible(this.rightBoot, pVisible); + setBoneVisible(this.leftBoot, pVisible); + } + + /** + * Gets and caches the relevant armor model bones for this baked model if it hasn't been done already + */ + public void grabRelevantBones(AzBakedModel model, AzArmorBoneProvider boneProvider) { + if (this.lastModel == model) { + return; + } + + this.lastModel = model; + this.head = boneProvider.getHeadBone(model); + this.body = boneProvider.getBodyBone(model); + this.rightArm = boneProvider.getRightArmBone(model); + this.leftArm = boneProvider.getLeftArmBone(model); + this.rightLeg = boneProvider.getRightLegBone(model); + this.leftLeg = boneProvider.getLeftLegBone(model); + this.rightBoot = boneProvider.getRightBootBone(model); + this.leftBoot = boneProvider.getLeftBootBone(model); + } + + /** + * Transform the currently rendering {@link AzBakedModel} to match the positions and rotations of the base model + */ + public void applyBaseTransformations(HumanoidModel baseModel) { + if (this.head != null) { + ModelPart headPart = baseModel.head; + + RenderUtils.matchModelPartRot(headPart, this.head); + this.head.updatePosition(headPart.x, -headPart.y, headPart.z); + } + + if (this.body != null) { + ModelPart bodyPart = baseModel.body; + + RenderUtils.matchModelPartRot(bodyPart, this.body); + this.body.updatePosition(bodyPart.x, -bodyPart.y, bodyPart.z); + } + + if (this.rightArm != null) { + ModelPart rightArmPart = baseModel.rightArm; + + RenderUtils.matchModelPartRot(rightArmPart, this.rightArm); + this.rightArm.updatePosition(rightArmPart.x + 5, 2 - rightArmPart.y, rightArmPart.z); + } + + if (this.leftArm != null) { + ModelPart leftArmPart = baseModel.leftArm; + + RenderUtils.matchModelPartRot(leftArmPart, this.leftArm); + this.leftArm.updatePosition(leftArmPart.x - 5f, 2f - leftArmPart.y, leftArmPart.z); + } + + if (this.rightLeg != null) { + ModelPart rightLegPart = baseModel.rightLeg; + + RenderUtils.matchModelPartRot(rightLegPart, this.rightLeg); + this.rightLeg.updatePosition(rightLegPart.x + 2, 12 - rightLegPart.y, rightLegPart.z); + + if (this.rightBoot != null) { + RenderUtils.matchModelPartRot(rightLegPart, this.rightBoot); + this.rightBoot.updatePosition(rightLegPart.x + 2, 12 - rightLegPart.y, rightLegPart.z); + } + } + + if (this.leftLeg != null) { + ModelPart leftLegPart = baseModel.leftLeg; + + RenderUtils.matchModelPartRot(leftLegPart, this.leftLeg); + this.leftLeg.updatePosition(leftLegPart.x - 2, 12 - leftLegPart.y, leftLegPart.z); + + if (this.leftBoot != null) { + RenderUtils.matchModelPartRot(leftLegPart, this.leftBoot); + this.leftBoot.updatePosition(leftLegPart.x - 2, 12 - leftLegPart.y, leftLegPart.z); + } + } + } + + /** + * Resets the bone visibility for the model based on the current {@link ModelPart} and {@link EquipmentSlot}, and + * then sets the bones relevant to the current part as visible for rendering.
+ *
+ * If you are rendering a geo entity with armor, you should probably be calling this prior to rendering + */ + public void applyBoneVisibilityByPart(EquipmentSlot currentSlot, ModelPart currentPart, HumanoidModel model) { + setAllVisible(false); + + currentPart.visible = true; + AzBone bone = null; + + if (currentPart == model.hat || currentPart == model.head) { + bone = this.head; + } else if (currentPart == model.body) { + bone = this.body; + } else if (currentPart == model.leftArm) { + bone = this.leftArm; + } else if (currentPart == model.rightArm) { + bone = this.rightArm; + } else if (currentPart == model.leftLeg) { + bone = currentSlot == EquipmentSlot.FEET ? this.leftBoot : this.leftLeg; + } else if (currentPart == model.rightLeg) { + bone = currentSlot == EquipmentSlot.FEET ? this.rightBoot : this.rightLeg; + } + + if (bone != null) { + bone.setHidden(false); + } + } + + /** + * Resets the bone visibility for the model based on the currently rendering slot, and then sets bones relevant to + * the current slot as visible for rendering.
+ *
+ * This is only called by default for non-geo entities (I.E. players or vanilla mobs) + */ + public void applyBoneVisibilityBySlot(EquipmentSlot currentSlot) { + setAllVisible(false); + + switch (currentSlot) { + case HEAD -> setBoneVisible(this.head, true); + case CHEST -> { + setBoneVisible(this.body, true); + setBoneVisible(this.rightArm, true); + setBoneVisible(this.leftArm, true); + } + case LEGS -> { + setBoneVisible(this.rightLeg, true); + setBoneVisible(this.leftLeg, true); + } + case FEET -> { + setBoneVisible(this.rightBoot, true); + setBoneVisible(this.leftBoot, true); + } + case MAINHAND, OFFHAND -> { /* NO-OP */ } + } + } + + /** + * Sets a bone as visible or hidden, with nullability + */ + protected void setBoneVisible(@Nullable AzBone bone, boolean visible) { + if (bone == null) + return; + + bone.setHidden(!visible); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneProvider.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneProvider.java new file mode 100644 index 000000000..05e09427e --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneProvider.java @@ -0,0 +1,96 @@ +package mod.azure.azurelib.rewrite.render.armor.bone; + +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.model.AzBone; +import org.jetbrains.annotations.Nullable; + +public interface AzArmorBoneProvider { + + String BONE_ARMOR_BODY_NAME = "armorBody"; + + String BONE_ARMOR_HEAD_NAME = "armorHead"; + + String BONE_ARMOR_LEFT_ARM_NAME = "armorLeftArm"; + + String BONE_ARMOR_RIGHT_ARM_NAME = "armorRightArm"; + + String BONE_ARMOR_LEFT_BOOT_NAME = "armorLeftBoot"; + + String BONE_ARMOR_RIGHT_BOOT_NAME = "armorRightBoot"; + + String BONE_ARMOR_LEFT_LEG_NAME = "armorLeftLeg"; + + String BONE_ARMOR_RIGHT_LEG_NAME = "armorRightLeg"; + + /** + * Returns the 'head' GeoBone from this model.
+ * Override if your geo model has different bone names for these bones + * + * @return The bone for the head model piece, or null if not using it + */ + @Nullable + AzBone getHeadBone(AzBakedModel model); + + /** + * Returns the 'body' GeoBone from this model.
+ * Override if your geo model has different bone names for these bones + * + * @return The bone for the body model piece, or null if not using it + */ + @Nullable + AzBone getBodyBone(AzBakedModel model); + + /** + * Returns the 'right arm' GeoBone from this model.
+ * Override if your geo model has different bone names for these bones + * + * @return The bone for the right arm model piece, or null if not using it + */ + @Nullable + AzBone getRightArmBone(AzBakedModel model); + + /** + * Returns the 'left arm' GeoBone from this model.
+ * Override if your geo model has different bone names for these bones + * + * @return The bone for the left arm model piece, or null if not using it + */ + @Nullable + AzBone getLeftArmBone(AzBakedModel model); + + /** + * Returns the 'right leg' GeoBone from this model.
+ * Override if your geo model has different bone names for these bones + * + * @return The bone for the right leg model piece, or null if not using it + */ + @Nullable + AzBone getRightLegBone(AzBakedModel model); + + /** + * Returns the 'left leg' GeoBone from this model.
+ * Override if your geo model has different bone names for these bones + * + * @return The bone for the left leg model piece, or null if not using it + */ + @Nullable + AzBone getLeftLegBone(AzBakedModel model); + + /** + * Returns the 'right boot' GeoBone from this model.
+ * Override if your geo model has different bone names for these bones + * + * @return The bone for the right boot model piece, or null if not using it + */ + @Nullable + AzBone getRightBootBone(AzBakedModel model); + + /** + * Returns the 'left boot' GeoBone from this model.
+ * Override if your geo model has different bone names for these bones + * + * @return The bone for the left boot model piece, or null if not using it + */ + @Nullable + AzBone getLeftBootBone(AzBakedModel model); +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzDefaultArmorBoneProvider.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzDefaultArmorBoneProvider.java new file mode 100644 index 000000000..7588f271f --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzDefaultArmorBoneProvider.java @@ -0,0 +1,48 @@ +package mod.azure.azurelib.rewrite.render.armor.bone; + +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.model.AzBone; +import org.jetbrains.annotations.Nullable; + +public class AzDefaultArmorBoneProvider implements AzArmorBoneProvider { + + @Nullable + public AzBone getHeadBone(AzBakedModel model) { + return model.getBoneOrNull(BONE_ARMOR_HEAD_NAME); + } + + @Nullable + public AzBone getBodyBone(AzBakedModel model) { + return model.getBoneOrNull(BONE_ARMOR_BODY_NAME); + } + + @Nullable + public AzBone getRightArmBone(AzBakedModel model) { + return model.getBoneOrNull(BONE_ARMOR_RIGHT_ARM_NAME); + } + + @Nullable + public AzBone getLeftArmBone(AzBakedModel model) { + return model.getBoneOrNull(BONE_ARMOR_LEFT_ARM_NAME); + } + + @Nullable + public AzBone getRightLegBone(AzBakedModel model) { + return model.getBoneOrNull(BONE_ARMOR_RIGHT_LEG_NAME); + } + + @Nullable + public AzBone getLeftLegBone(AzBakedModel model) { + return model.getBoneOrNull(BONE_ARMOR_LEFT_LEG_NAME); + } + + @Nullable + public AzBone getRightBootBone(AzBakedModel model) { + return model.getBoneOrNull(BONE_ARMOR_RIGHT_BOOT_NAME); + } + + @Nullable + public AzBone getLeftBootBone(AzBakedModel model) { + return model.getBoneOrNull(BONE_ARMOR_LEFT_BOOT_NAME); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityModelRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityModelRenderer.java new file mode 100644 index 000000000..fc03b2d20 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityModelRenderer.java @@ -0,0 +1,155 @@ +package mod.azure.azurelib.rewrite.render.block; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.AzLayerRenderer; +import mod.azure.azurelib.rewrite.render.AzModelRenderer; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.DirectionalBlock; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; +import org.joml.Vector3f; + +/** + * The AzBlockEntityModelRenderer is a specialized model renderer class for rendering block entities in a 3D space. It + * extends the AzModelRenderer class and provides functionality specific to handling and rendering block entities based + * on their corresponding properties and transformations. + * + * @param The type of BlockEntity that this renderer is responsible for + */ +public class AzBlockEntityModelRenderer extends AzModelRenderer { + + private final AzBlockEntityRendererPipeline blockEntityRendererPipeline; + + public AzBlockEntityModelRenderer( + AzBlockEntityRendererPipeline blockEntityRendererPipeline, + AzLayerRenderer layerRenderer + ) { + super(blockEntityRendererPipeline, layerRenderer); + this.blockEntityRendererPipeline = blockEntityRendererPipeline; + } + + /** + * The actual render method that subtype renderers should override to handle their specific rendering tasks.
+ * {@link AzBlockEntityRendererPipeline#preRender} has already been called by this stage, and + * {@link AzBlockEntityRendererPipeline#postRender} will be called directly after + */ + @Override + public void render(AzRendererPipelineContext context, boolean isReRender) { + var entity = context.animatable(); + var poseStack = context.poseStack(); + + if (!isReRender) { + rotateBlock(getFacing(entity), poseStack); + + poseStack.translate(0.5, 0, 0.5); + var animator = blockEntityRendererPipeline.getRenderer().getAnimator(); + + if (animator != null) { + animator.animate(entity, context.partialTick()); + } + } + + blockEntityRendererPipeline.modelRenderTranslations = new Matrix4f(poseStack.last().pose()); + + var textureLocation = blockEntityRendererPipeline.config().textureLocation(entity); + RenderSystem.setShaderTexture(0, textureLocation); + super.render(context, isReRender); + } + + /** + * Renders the provided {@link AzBone} and its associated child bones + */ + @Override + public void renderRecursively(AzRendererPipelineContext context, AzBone bone, boolean isReRender) { + var buffer = context.vertexConsumer(); + var bufferSource = context.multiBufferSource(); + var entity = context.animatable(); + var poseStack = context.poseStack(); + var renderType = context.renderType(); + + poseStack.pushPose(); + RenderUtils.translateMatrixToBone(poseStack, bone); + RenderUtils.translateToPivotPoint(poseStack, bone); + RenderUtils.rotateMatrixAroundBone(poseStack, bone); + RenderUtils.scaleMatrixForBone(poseStack, bone); + + if (bone.isTrackingMatrices()) { + Matrix4f poseState = new Matrix4f(poseStack.last().pose()); + Matrix4f localMatrix = RenderUtils.invertAndMultiplyMatrices( + poseState, + blockEntityRendererPipeline.entityRenderTranslations + ); + + bone.setModelSpaceMatrix( + RenderUtils.invertAndMultiplyMatrices(poseState, blockEntityRendererPipeline.modelRenderTranslations) + ); + bone.setLocalSpaceMatrix( + RenderUtils.translateMatrix(localMatrix, Vec3.ZERO.toVector3f()) + ); + bone.setWorldSpaceMatrix( + RenderUtils.translateMatrix( + new Matrix4f(localMatrix), + new Vector3f( + entity.getBlockPos().getX(), + entity.getBlockPos().getY(), + entity.getBlockPos().getZ() + ) + ) + ); + } + + RenderUtils.translateAwayFromPivotPoint(poseStack, bone); + + if (!isReRender && buffer instanceof BufferBuilder builder && !builder.building) { + context.setVertexConsumer(bufferSource.getBuffer(renderType)); + } + + renderCubesOfBone(context, bone); + + if (!isReRender) { + layerRenderer.applyRenderLayersForBone(context, bone); + } + + renderChildBones(context, bone, isReRender); + + poseStack.popPose(); + } + + /** + * Attempt to extract a direction from the block so that the model can be oriented correctly + */ + protected Direction getFacing(T block) { + BlockState blockState = block.getBlockState(); + + if (blockState.hasProperty(HorizontalDirectionalBlock.FACING)) + return blockState.getValue(HorizontalDirectionalBlock.FACING); + + if (blockState.hasProperty(DirectionalBlock.FACING)) + return blockState.getValue(DirectionalBlock.FACING); + + return Direction.NORTH; + } + + /** + * Rotate the {@link PoseStack} based on the determined {@link Direction} the block is facing + */ + protected void rotateBlock(Direction facing, PoseStack poseStack) { + switch (facing) { + case SOUTH -> poseStack.mulPose(Axis.YP.rotationDegrees(180)); + case WEST -> poseStack.mulPose(Axis.YP.rotationDegrees(90)); + case NORTH -> poseStack.mulPose(Axis.YP.rotationDegrees(0)); + case EAST -> poseStack.mulPose(Axis.YP.rotationDegrees(270)); + case UP -> poseStack.mulPose(Axis.XP.rotationDegrees(90)); + case DOWN -> poseStack.mulPose(Axis.XN.rotationDegrees(90)); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRenderer.java new file mode 100644 index 000000000..687f6383d --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRenderer.java @@ -0,0 +1,64 @@ +package mod.azure.azurelib.rewrite.render.block; + +import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.rewrite.animation.impl.AzBlockAnimator; +import mod.azure.azurelib.rewrite.render.AzProvider; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The {@code AzBlockEntityRenderer} class is an abstract base class for rendering custom block entities. It leverages + * an animation and rendering pipeline mechanism to provide extended functionalities, such as dynamic animations and + * model customization. + * + * @param The specific type of {@link BlockEntity} that this renderer processes. + */ +public abstract class AzBlockEntityRenderer implements BlockEntityRenderer { + + private final AzProvider provider; + + private final AzBlockEntityRendererPipeline rendererPipeline; + + @Nullable + private AzBlockAnimator reusedAzBlockAnimator; + + protected AzBlockEntityRenderer(AzBlockEntityRendererConfig config) { + this.provider = new AzProvider<>(config::createAnimator, config::modelLocation); + this.rendererPipeline = createPipeline(config); + } + + protected AzBlockEntityRendererPipeline createPipeline(AzBlockEntityRendererConfig config) { + return new AzBlockEntityRendererPipeline<>(config, this); + } + + @Override + public void render( + @NotNull T entity, + float partialTick, + @NotNull PoseStack poseStack, + @NotNull MultiBufferSource source, + int packedLight, + int packedOverlay + ) { + var cachedEntityAnimator = (AzBlockAnimator) provider.provideAnimator(entity); + var model = provider.provideBakedModel(entity); + var context = rendererPipeline.context(); + + if (cachedEntityAnimator != null && model != null) { + cachedEntityAnimator.setActiveModel(model); + } + + // Point the renderer's current animator reference to the cached entity animator before rendering. + reusedAzBlockAnimator = cachedEntityAnimator; + + // Execute the render pipeline. + rendererPipeline.render(poseStack, model, entity, source, null, null, 0, partialTick, packedLight, context.red(), context.green(), context.blue(), context.alpha()); + } + + public AzBlockAnimator getAnimator() { + return reusedAzBlockAnimator; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java new file mode 100644 index 000000000..c9d26c437 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java @@ -0,0 +1,92 @@ +package mod.azure.azurelib.rewrite.render.block; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * The {@code AzBlockEntityRendererConfig} class is a specialized configuration for rendering block entities. It extends + * the generic {@link AzRendererConfig} and provides additional methods to streamline the creation of configurations + * specifically for block entity renderers. + * + * @param The type of block entity this configuration is tailored for. + */ +public class AzBlockEntityRendererConfig extends AzRendererConfig { + + private AzBlockEntityRendererConfig( + Supplier> animatorProvider, + Function modelLocationProvider, + Function renderTypeFunction, + List> renderLayers, + Function textureLocationProvider, + float scaleHeight, + float scaleWidth + ) { + super( + animatorProvider, + modelLocationProvider, + renderTypeFunction, + renderLayers, + textureLocationProvider, + scaleHeight, + scaleWidth + ); + } + + public static Builder builder( + ResourceLocation modelLocation, + ResourceLocation textureLocation + ) { + return new Builder<>($ -> modelLocation, $ -> textureLocation); + } + + public static Builder builder( + Function modelLocationProvider, + Function textureLocationProvider + ) { + return new Builder<>(modelLocationProvider, textureLocationProvider); + } + + public static class Builder extends AzRendererConfig.Builder { + + protected Builder( + Function modelLocationProvider, + Function textureLocationProvider + ) { + super(modelLocationProvider, textureLocationProvider); + } + + @Override + public Builder addRenderLayer(AzRenderLayer renderLayer) { + return (Builder) super.addRenderLayer(renderLayer); + } + + @Override + public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animatorProvider) { + return (Builder) super.setAnimatorProvider(animatorProvider); + } + + @Override + public AzBlockEntityRendererConfig build() { + var baseConfig = super.build(); + + return new AzBlockEntityRendererConfig<>( + baseConfig::createAnimator, + baseConfig::modelLocation, + baseConfig::getRenderType, + baseConfig.renderLayers(), + baseConfig::textureLocation, + baseConfig.scaleHeight(), + baseConfig.scaleWidth() + ); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java new file mode 100644 index 000000000..b12169ee1 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java @@ -0,0 +1,86 @@ +package mod.azure.azurelib.rewrite.render.block; + +import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.cache.texture.AnimatableTexture; +import mod.azure.azurelib.rewrite.render.*; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.joml.Matrix4f; + +/** + * AzBlockEntityRendererPipeline is a specific implementation of the {@link AzRendererPipeline} tailored for rendering + * block entities. It manages the rendering pipeline with customized configurations and rendering behavior for block + * entities, while integrating with the parent pipeline logic. + * + * @param The type of {@link BlockEntity} that this renderer pipeline is designed to render. + */ +public class AzBlockEntityRendererPipeline extends AzRendererPipeline { + + private final AzBlockEntityRenderer blockEntityRenderer; + + protected Matrix4f entityRenderTranslations = new Matrix4f(); + + protected Matrix4f modelRenderTranslations = new Matrix4f(); + + public AzBlockEntityRendererPipeline( + AzBlockEntityRendererConfig config, + AzBlockEntityRenderer blockEntityRenderer + ) { + super(config); + this.blockEntityRenderer = blockEntityRenderer; + } + + @Override + protected AzBlockEntityRendererPipelineContext createContext(AzRendererPipeline rendererPipeline) { + return new AzBlockEntityRendererPipelineContext<>(this); + } + + @Override + protected AzModelRenderer createModelRenderer(AzLayerRenderer layerRenderer) { + return new AzBlockEntityModelRenderer<>(this, layerRenderer); + } + + @Override + protected AzLayerRenderer createLayerRenderer(AzRendererConfig config) { + return new AzLayerRenderer<>(config::renderLayers); + } + + /** + * Update the current frame of a {@link AnimatableTexture potentially animated} texture used by this + * GeoRenderer.
+ * This should only be called immediately prior to rendering, and only + * + * @see AnimatableTexture#setAndUpdate(ResourceLocation, int) + */ + @Override + public void updateAnimatedTextureFrame(T entity) { + AnimatableTexture.setAndUpdate( + config.textureLocation(entity), + entity.getBlockPos().getX() + entity.getBlockPos().getY() + entity.getBlockPos().getZ() + + (int) RenderUtils.getCurrentTick() + ); + } + + /** + * Called before rendering the model to buffer. Allows for render modifications and preparatory work such as scaling + * and translating.
+ * {@link PoseStack} translations made here are kept until the end of the render process + */ + @Override + public void preRender(AzRendererPipelineContext context, boolean isReRender) { + var poseStack = context.poseStack(); + this.entityRenderTranslations.set(poseStack.last().pose()); + + var scaleWidth = config.scaleWidth(); + var scaleHeight = config.scaleHeight(); + scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); + } + + @Override + public void postRender(AzRendererPipelineContext context, boolean isReRender) {} + + public AzBlockEntityRenderer getRenderer() { + return blockEntityRenderer; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipelineContext.java new file mode 100644 index 000000000..e5939ca88 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipelineContext.java @@ -0,0 +1,34 @@ +package mod.azure.azurelib.rewrite.render.block; + +import mod.azure.azurelib.rewrite.render.AzRendererPipeline; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a specialized rendering context for handling {@link BlockEntity} rendering in a pipeline-based rendering + * framework. This class extends {@link AzRendererPipelineContext} to provide specific functionality tailored to block + * entities within the AzureLib rendering system. + * + * @param The type of {@link BlockEntity} to be rendered. + */ +public class AzBlockEntityRendererPipelineContext extends AzRendererPipelineContext { + + public AzBlockEntityRendererPipelineContext(AzRendererPipeline rendererPipeline) { + super(rendererPipeline); + } + + @Override + public @NotNull RenderType getDefaultRenderType( + T animatable, + ResourceLocation texture, + @Nullable MultiBufferSource bufferSource, + float partialTick + ) { + return RenderType.entityCutoutNoCull(texture); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLayerRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLayerRenderer.java new file mode 100644 index 000000000..3076cd1a7 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLayerRenderer.java @@ -0,0 +1,34 @@ +package mod.azure.azurelib.rewrite.render.entity; + +import mod.azure.azurelib.rewrite.render.AzLayerRenderer; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; +import net.minecraft.world.entity.Entity; + +import java.util.Collection; +import java.util.function.Supplier; + +/** + * A renderer class responsible for rendering additional entity layers for a particular animatable entity type. It + * extends functionality from {@link AzLayerRenderer} and enables conditional rendering based on entity states. + * + * @param The type of animatable entity this renderer is applied to. + */ +public class AzEntityLayerRenderer extends AzLayerRenderer { + + public AzEntityLayerRenderer(Supplier>> renderLayerSupplier) { + super(renderLayerSupplier); + } + + /** + * Render the various {@link AzRenderLayer RenderLayers} that have been registered to this renderer + */ + @Override + public void applyRenderLayers(AzRendererPipelineContext context) { + var animatable = context.animatable(); + + if (!animatable.isSpectator()) { + super.applyRenderLayers(context); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLeashRenderUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLeashRenderUtil.java new file mode 100644 index 000000000..48c6535cc --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLeashRenderUtil.java @@ -0,0 +1,159 @@ +package mod.azure.azurelib.rewrite.render.entity; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; + +/** + * Utility class for rendering entity leash visuals within the Minecraft rendering engine. This class provides static + * methods to handle leash rendering logic, enabling flexible re-use and separation from the default rendering behavior. + *
+ * This utility replicates the leash rendering logic from {@link net.minecraft.client.renderer.entity.MobRenderer} to + * provide enhanced customization for entity rendering purposes. + */ +public class AzEntityLeashRenderUtil { + + /** + * Static rendering code for rendering a leash segment.
+ * It's a like-for-like from {@link net.minecraft.client.renderer.entity.MobRenderer#renderLeash} that had to be + * duplicated here for flexible usage + */ + public static void renderLeash( + AzEntityRenderer azEntityRenderer, + M mob, + float partialTick, + PoseStack poseStack, + MultiBufferSource bufferSource, + E leashHolder + ) { + double lerpBodyAngle = (Mth.lerp(partialTick, mob.yBodyRotO, mob.yBodyRot) * Mth.DEG_TO_RAD) + Mth.HALF_PI; + Vec3 leashOffset = mob.getLeashOffset(); + double xAngleOffset = Math.cos(lerpBodyAngle) * leashOffset.z + Math.sin(lerpBodyAngle) * leashOffset.x; + double zAngleOffset = Math.sin(lerpBodyAngle) * leashOffset.z - Math.cos(lerpBodyAngle) * leashOffset.x; + double lerpOriginX = Mth.lerp(partialTick, mob.xo, mob.getX()) + xAngleOffset; + double lerpOriginY = Mth.lerp(partialTick, mob.yo, mob.getY()) + leashOffset.y; + double lerpOriginZ = Mth.lerp(partialTick, mob.zo, mob.getZ()) + zAngleOffset; + Vec3 ropeGripPosition = leashHolder.getRopeHoldPosition(partialTick); + float xDif = (float) (ropeGripPosition.x - lerpOriginX); + float yDif = (float) (ropeGripPosition.y - lerpOriginY); + float zDif = (float) (ropeGripPosition.z - lerpOriginZ); + float offsetMod = Mth.invSqrt(xDif * xDif + zDif * zDif) * 0.025f / 2f; + float xOffset = zDif * offsetMod; + float zOffset = xDif * offsetMod; + VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.leash()); + BlockPos entityEyePos = BlockPos.containing(mob.getEyePosition(partialTick)); + BlockPos holderEyePos = BlockPos.containing(leashHolder.getEyePosition(partialTick)); + int entityBlockLight = azEntityRenderer.getBlockLightLevel((T) mob, entityEyePos); + int holderBlockLight = leashHolder.isOnFire() + ? 15 + : leashHolder.level() + .getBrightness( + LightLayer.BLOCK, + holderEyePos + ); + int entitySkyLight = mob.level().getBrightness(LightLayer.SKY, entityEyePos); + int holderSkyLight = mob.level().getBrightness(LightLayer.SKY, holderEyePos); + + poseStack.pushPose(); + poseStack.translate(xAngleOffset, leashOffset.y, zAngleOffset); + + Matrix4f posMatrix = new Matrix4f(poseStack.last().pose()); + + for (int segment = 0; segment <= 24; ++segment) { + renderLeashPiece( + vertexConsumer, + posMatrix, + xDif, + yDif, + zDif, + entityBlockLight, + holderBlockLight, + entitySkyLight, + holderSkyLight, + 0.025f, + 0.025f, + xOffset, + zOffset, + segment, + false + ); + } + + for (int segment = 24; segment >= 0; --segment) { + renderLeashPiece( + vertexConsumer, + posMatrix, + xDif, + yDif, + zDif, + entityBlockLight, + holderBlockLight, + entitySkyLight, + holderSkyLight, + 0.025f, + 0.0f, + xOffset, + zOffset, + segment, + true + ); + } + + poseStack.popPose(); + } + + /** + * Static rendering code for rendering a leash segment.
+ * It's a like-for-like from {@link net.minecraft.client.renderer.entity.MobRenderer#addVertexPair} that had to be + * duplicated here for flexible usage + */ + private static void renderLeashPiece( + VertexConsumer buffer, + Matrix4f positionMatrix, + float xDif, + float yDif, + float zDif, + int entityBlockLight, + int holderBlockLight, + int entitySkyLight, + int holderSkyLight, + float width, + float yOffset, + float xOffset, + float zOffset, + int segment, + boolean isLeashKnot + ) { + var piecePosPercent = segment / 24f; + var lerpBlockLight = (int) Mth.lerp(piecePosPercent, entityBlockLight, holderBlockLight); + var lerpSkyLight = (int) Mth.lerp(piecePosPercent, entitySkyLight, holderSkyLight); + var packedLight = LightTexture.pack(lerpBlockLight, lerpSkyLight); + var knotColourMod = segment % 2 == (isLeashKnot ? 1 : 0) ? 0.7f : 1f; + var red = 0.5f * knotColourMod; + var green = 0.4f * knotColourMod; + var blue = 0.3f * knotColourMod; + var x = xDif * piecePosPercent; + var y = yDif > 0.0f + ? yDif * piecePosPercent * piecePosPercent + : yDif - yDif * (1.0f - piecePosPercent) * (1.0f - piecePosPercent); + var z = zDif * piecePosPercent; + + buffer.vertex(positionMatrix, x - xOffset, y + yOffset, z + zOffset).color(red, green, blue, 1).uv2( + packedLight).endVertex(); + buffer.vertex(positionMatrix, x + xOffset, y + width - yOffset, z - zOffset).color(red, green, blue, 1).uv2( + packedLight).endVertex(); + } + + private AzEntityLeashRenderUtil() { + throw new UnsupportedOperationException(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityModelRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityModelRenderer.java new file mode 100644 index 000000000..9f693aab7 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityModelRenderer.java @@ -0,0 +1,275 @@ +package mod.azure.azurelib.rewrite.render.entity; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.AzLayerRenderer; +import mod.azure.azurelib.rewrite.render.AzModelRenderer; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Pose; +import org.joml.Matrix4f; + +/** + * AzEntityModelRenderer is a class responsible for rendering animated 3D entity models in a pipeline-based rendering + * setup. Extends the {@link AzModelRenderer} class and utilizes the {@link AzEntityRendererPipeline} to handle various + * rendering tasks, such as applying model transformations and managing animated states in the rendering lifecycle.
+ * + * @param The type of entity that this renderer applies to, extends the {@link Entity} class. + */ +public class AzEntityModelRenderer extends AzModelRenderer { + + private final AzEntityRendererPipeline entityRendererPipeline; + + public AzEntityModelRenderer(AzEntityRendererPipeline entityRendererPipeline, AzLayerRenderer layerRenderer) { + super(entityRendererPipeline, layerRenderer); + this.entityRendererPipeline = entityRendererPipeline; + } + + /** + * The actual render method that subtype renderers should override to handle their specific rendering tasks.
+ * {@link AzEntityRendererPipeline#preRender} has already been called by this stage, and + * {@link AzEntityRendererPipeline#postRender} will be called directly after + */ + @Override + public void render(AzRendererPipelineContext context, boolean isReRender) { + var animatable = context.animatable(); + var partialTick = context.partialTick(); + var poseStack = context.poseStack(); + + poseStack.pushPose(); + + LivingEntity livingEntity = animatable instanceof LivingEntity entity ? entity : null; + + boolean shouldSit = animatable.isPassenger() && (animatable.getVehicle() != null); + float lerpBodyRot = livingEntity == null + ? 0 + : Mth.rotLerp( + partialTick, + livingEntity.yBodyRotO, + livingEntity.yBodyRot + ); + float lerpHeadRot = livingEntity == null + ? 0 + : Mth.rotLerp( + partialTick, + livingEntity.yHeadRotO, + livingEntity.yHeadRot + ); + float netHeadYaw = lerpHeadRot - lerpBodyRot; + + if (shouldSit && animatable.getVehicle() instanceof LivingEntity livingentity) { + lerpBodyRot = Mth.rotLerp(partialTick, livingentity.yBodyRotO, livingentity.yBodyRot); + netHeadYaw = lerpHeadRot - lerpBodyRot; + float clampedHeadYaw = Mth.clamp(Mth.wrapDegrees(netHeadYaw), -85, 85); + lerpBodyRot = lerpHeadRot - clampedHeadYaw; + + if (clampedHeadYaw * clampedHeadYaw > 2500f) + lerpBodyRot += clampedHeadYaw * 0.2f; + + netHeadYaw = lerpHeadRot - lerpBodyRot; + } + + if (animatable.getPose() == Pose.SLEEPING && livingEntity != null) { + Direction bedDirection = livingEntity.getBedOrientation(); + + if (bedDirection != null) { + float eyePosOffset = livingEntity.getEyeHeight(Pose.STANDING) - 0.1F; + + poseStack.translate( + -bedDirection.getStepX() * eyePosOffset, + 0, + -bedDirection.getStepZ() * eyePosOffset + ); + } + } + + float nativeScale = livingEntity != null ? livingEntity.getScale() : 1; + float ageInTicks = animatable.tickCount + partialTick; + float limbSwingAmount = 0; + float limbSwing = 0; + + poseStack.scale(nativeScale, nativeScale, nativeScale); + applyRotations(animatable, poseStack, ageInTicks, lerpBodyRot, partialTick, nativeScale); + + if (!shouldSit && animatable.isAlive() && livingEntity != null) { + limbSwingAmount = Mth.lerp( + partialTick, + livingEntity.walkAnimation.speedOld, + livingEntity.walkAnimation.speed() + ); + limbSwing = livingEntity.walkAnimation.position() - livingEntity.walkAnimation.speed() * (1 - partialTick); + + if (livingEntity.isBaby()) { + limbSwing *= 3f; + } + + if (limbSwingAmount > 1f) { + limbSwingAmount = 1f; + } + } + + if (!isReRender) { + // FIXME: Figure out what to do with this data stuff. + // float headPitch = Mth.lerp(partialTick, animatable.xRotO, animatable.getXRot()); + // var velocity = animatable.getDeltaMovement(); + // float avgVelocity = (float) (Math.abs(velocity.x) + Math.abs(velocity.z) / 2f); + // + // long instanceId = getInstanceId(animatable); + // + // animationState.setData(DataTickets.TICK, animatable.getTick(animatable)); + // animationState.setData(DataTickets.ENTITY, animatable); + // animationState.setData( + // DataTickets.ENTITY_MODEL_DATA, + // new EntityModelData( + // shouldSit, + // livingEntity != null && livingEntity.isBaby(), + // -netHeadYaw, + // -headPitch + // ) + // ); + // + // this.model.addAdditionalStateData(animatable, instanceId, animationState::setData); + + var animator = entityRendererPipeline.getRenderer().getAnimator(); + + if (animator != null) { + animator.animate(animatable, context.partialTick()); + } + } + + entityRendererPipeline.modelRenderTranslations.set(poseStack.last().pose()); + + if (!animatable.isInvisibleTo(Minecraft.getInstance().player)) { + super.render(context, isReRender); + } + + poseStack.popPose(); + } + + /** + * Renders the provided {@link AzBone} and its associated child bones + */ + @Override + public void renderRecursively(AzRendererPipelineContext context, AzBone bone, boolean isReRender) { + var buffer = context.vertexConsumer(); + var bufferSource = context.multiBufferSource(); + var entity = context.animatable(); + var poseStack = context.poseStack(); + var renderType = context.renderType(); + + poseStack.pushPose(); + RenderUtils.translateMatrixToBone(poseStack, bone); + RenderUtils.translateToPivotPoint(poseStack, bone); + RenderUtils.rotateMatrixAroundBone(poseStack, bone); + RenderUtils.scaleMatrixForBone(poseStack, bone); + + if (bone.isTrackingMatrices()) { + Matrix4f poseState = new Matrix4f(poseStack.last().pose()); + Matrix4f localMatrix = RenderUtils.invertAndMultiplyMatrices( + poseState, + entityRendererPipeline.entityRenderTranslations + ); + + bone.setModelSpaceMatrix( + RenderUtils.invertAndMultiplyMatrices(poseState, entityRendererPipeline.modelRenderTranslations) + ); + bone.setLocalSpaceMatrix( + RenderUtils.translateMatrix( + localMatrix, + entityRendererPipeline.getRenderer().getRenderOffset(entity, 1).toVector3f() + ) + ); + bone.setWorldSpaceMatrix( + RenderUtils.translateMatrix(new Matrix4f(localMatrix), entity.position().toVector3f()) + ); + } + + RenderUtils.translateAwayFromPivotPoint(poseStack, bone); + + if (!isReRender && buffer instanceof BufferBuilder builder && !builder.building) { + context.setVertexConsumer(bufferSource.getBuffer(renderType)); + } + + renderCubesOfBone(context, bone); + + if (!isReRender) { + layerRenderer.applyRenderLayersForBone(context, bone); + } + + renderChildBones(context, bone, isReRender); + + poseStack.popPose(); + } + + /** + * Applies rotation transformations to the renderer prior to render time to account for various entity states, + * default scale of 1 + */ + protected void applyRotations( + T animatable, + PoseStack poseStack, + float ageInTicks, + float rotationYaw, + float partialTick + ) { + applyRotations(animatable, poseStack, ageInTicks, rotationYaw, partialTick, 1); + } + + /** + * Applies rotation transformations to the renderer prior to render time to account for various entity states, + * scalable + */ + protected void applyRotations( + T animatable, + PoseStack poseStack, + float ageInTicks, + float rotationYaw, + float partialTick, + float nativeScale + ) { + if (animatable.isFullyFrozen()) { + rotationYaw += (float) (Math.cos(animatable.tickCount * 3.25d) * Math.PI * 0.4d); + } + + if (!animatable.hasPose(Pose.SLEEPING)) { + poseStack.mulPose(Axis.YP.rotationDegrees(180f - rotationYaw)); + } + + if (animatable instanceof LivingEntity livingEntity) { + var config = entityRendererPipeline.getRenderer().config(); + var deathMaxRotation = config.getDeathMaxRotation(animatable); + + if (livingEntity.deathTime > 0) { + float deathRotation = (livingEntity.deathTime + partialTick - 1f) / 20f * 1.6f; + + poseStack.mulPose( + Axis.ZP.rotationDegrees(Math.min(Mth.sqrt(deathRotation), 1) * deathMaxRotation) + ); + } else if (livingEntity.isAutoSpinAttack()) { + poseStack.mulPose(Axis.XP.rotationDegrees(-90f - livingEntity.getXRot())); + poseStack.mulPose(Axis.YP.rotationDegrees((livingEntity.tickCount + partialTick) * -75f)); + } else if (animatable.hasPose(Pose.SLEEPING)) { + Direction bedOrientation = livingEntity.getBedOrientation(); + + poseStack.mulPose( + Axis.YP.rotationDegrees( + bedOrientation != null ? RenderUtils.getDirectionAngle(bedOrientation) : rotationYaw + ) + ); + poseStack.mulPose(Axis.ZP.rotationDegrees(deathMaxRotation)); + poseStack.mulPose(Axis.YP.rotationDegrees(270f)); + } else if (LivingEntityRenderer.isEntityUpsideDown(livingEntity)) { + poseStack.translate(0, (animatable.getBbHeight() + 0.1f) / nativeScale, 0); + poseStack.mulPose(Axis.ZP.rotationDegrees(180f)); + } + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityNameRenderUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityNameRenderUtil.java new file mode 100644 index 000000000..44fced46b --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityNameRenderUtil.java @@ -0,0 +1,60 @@ +package mod.azure.azurelib.rewrite.render.entity; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; + +import java.util.Objects; + +public class AzEntityNameRenderUtil { + + public static boolean shouldShowName(EntityRenderDispatcher entityRenderDispatcher, T entity) { + var nameRenderDistance = entity.isDiscrete() ? 32d : 64d; + + if (!(entity instanceof LivingEntity)) { + return false; + } + + if (entityRenderDispatcher.distanceToSqr(entity) >= nameRenderDistance * nameRenderDistance) { + return false; + } + + if ( + entity instanceof Mob && (!entity.shouldShowName() && (!entity.hasCustomName() + || entity != entityRenderDispatcher.crosshairPickEntity)) + ) { + return false; + } + + final var minecraft = Minecraft.getInstance(); + // TODO: See if we can do this null check better. + var player = Objects.requireNonNull(minecraft.player); + var visibleToClient = !entity.isInvisibleTo(player); + var entityTeam = entity.getTeam(); + + if (entityTeam == null) { + return Minecraft.renderNames() && entity != minecraft.getCameraEntity() && visibleToClient + && !entity.isVehicle(); + } + + var playerTeam = minecraft.player.getTeam(); + + return switch (entityTeam.getNameTagVisibility()) { + case ALWAYS -> visibleToClient; + case NEVER -> false; + case HIDE_FOR_OTHER_TEAMS -> playerTeam == null + ? visibleToClient + : entityTeam.isAlliedTo( + playerTeam + ) && (entityTeam.canSeeFriendlyInvisibles() || visibleToClient); + case HIDE_FOR_OWN_TEAM -> + playerTeam == null ? visibleToClient : !entityTeam.isAlliedTo(playerTeam) && visibleToClient; + }; + } + + private AzEntityNameRenderUtil() { + throw new UnsupportedOperationException(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java new file mode 100644 index 000000000..2be472e15 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java @@ -0,0 +1,123 @@ +package mod.azure.azurelib.rewrite.render.entity; + +import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.rewrite.animation.impl.AzEntityAnimator; +import mod.azure.azurelib.rewrite.render.AzProvider; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * AzEntityRenderer is an abstract class responsible for rendering entities in the game. It extends the base + * functionality of {@link EntityRenderer} to provide additional rendering capabilities specific to animated and custom + * entities. This class is parameterized with a generic type {@code T}, which must extend {@link Entity}. It integrates + * several abstractions such as animation management, model caching, and advanced rendering pipelines for handling + * complex rendering behavior. Users are expected to configure this renderer using an {@link AzEntityRendererConfig}. + * Key components: - {@link AzEntityRendererConfig}: Defines configuration options such as textures, models, and + * animator providers. - {@link AzProvider}: Supplies baked models and animators for entities. - + * {@link AzEntityRendererPipeline}: Manages rendering logic through a custom pipeline. + */ +public abstract class AzEntityRenderer extends EntityRenderer { + + private final AzEntityRendererConfig config; + + private final AzProvider provider; + + private final AzEntityRendererPipeline rendererPipeline; + + @Nullable + private AzEntityAnimator reusedAzEntityAnimator; + + protected AzEntityRenderer(AzEntityRendererConfig config, EntityRendererProvider.Context context) { + super(context); + this.config = config; + this.provider = new AzProvider<>(config::createAnimator, config::modelLocation); + this.rendererPipeline = createPipeline(config); + } + + protected AzEntityRendererPipeline createPipeline(AzEntityRendererConfig config) { + return new AzEntityRendererPipeline<>(config, this); + } + + @Override + public final @NotNull ResourceLocation getTextureLocation(@NotNull T animatable) { + return config.textureLocation(animatable); + } + + public void superRender( + @NotNull T entity, + float entityYaw, + float partialTick, + @NotNull PoseStack poseStack, + @NotNull MultiBufferSource bufferSource, + int packedLight + ) { + super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight); + } + + @Override + public void render( + @NotNull T entity, + float entityYaw, + float partialTick, + @NotNull PoseStack poseStack, + @NotNull MultiBufferSource bufferSource, + int packedLight + ) { + var cachedEntityAnimator = (AzEntityAnimator) provider.provideAnimator(entity); + var azBakedModel = provider.provideBakedModel(entity); + var context = rendererPipeline.context(); + + if (cachedEntityAnimator != null && azBakedModel != null) { + cachedEntityAnimator.setActiveModel(azBakedModel); + } + + // Point the renderer's current animator reference to the cached entity animator before rendering. + reusedAzEntityAnimator = cachedEntityAnimator; + + // Execute the render pipeline. + rendererPipeline.render( + poseStack, + azBakedModel, + entity, + bufferSource, + null, + null, + entityYaw, + partialTick, + packedLight, + context.red(), + context.green(), + context.blue(), + context.alpha() + ); + } + + /** + * Whether the entity's nametag should be rendered or not.
+ * Pretty much exclusively used in {@link EntityRenderer#renderNameTag} + */ + @Override + public boolean shouldShowName(@NotNull T entity) { + return AzEntityNameRenderUtil.shouldShowName(entityRenderDispatcher, entity); + } + + // Proxy method override for super.getBlockLightLevel external access. + @Override + public int getBlockLightLevel(@NotNull T entity, @NotNull BlockPos pos) { + return super.getBlockLightLevel(entity, pos); + } + + public AzEntityAnimator getAnimator() { + return reusedAzEntityAnimator; + } + + public AzEntityRendererConfig config() { + return config; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java new file mode 100644 index 000000000..452318aa8 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java @@ -0,0 +1,119 @@ +package mod.azure.azurelib.rewrite.render.entity; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Configures the rendering behavior for custom entities in the game. This extends {@link AzRendererConfig}, adding + * extra functionality specifically for handling entity death rotations. + * + * @param the entity type this configuration applies to, extending {@link Entity} + */ +public class AzEntityRendererConfig extends AzRendererConfig { + + private final Function deathMaxRotationProvider; + + private AzEntityRendererConfig( + Supplier> animatorProvider, + Function deathMaxRotationProvider, + Function modelLocationProvider, + Function renderTypeFunction, + List> renderLayers, + Function textureLocationProvider, + float scaleHeight, + float scaleWidth + ) { + super( + animatorProvider, + modelLocationProvider, + renderTypeFunction, + renderLayers, + textureLocationProvider, + scaleHeight, + scaleWidth + ); + this.deathMaxRotationProvider = deathMaxRotationProvider; + } + + public float getDeathMaxRotation(T entity) { + return deathMaxRotationProvider.apply(entity); + } + + public static Builder builder( + ResourceLocation modelLocation, + ResourceLocation textureLocation + ) { + return new Builder<>($ -> modelLocation, $ -> textureLocation); + } + + public static Builder builder( + Function modelLocationProvider, + Function textureLocationProvider + ) { + return new Builder<>(modelLocationProvider, textureLocationProvider); + } + + public static class Builder extends AzRendererConfig.Builder { + + private Function deathMaxRotationProvider; + + protected Builder( + Function modelLocationProvider, + Function textureLocationProvider + ) { + super(modelLocationProvider, textureLocationProvider); + this.deathMaxRotationProvider = $ -> 90F; + } + + @Override + public Builder addRenderLayer(AzRenderLayer renderLayer) { + return (Builder) super.addRenderLayer(renderLayer); + } + + @Override + public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animatorProvider) { + return (Builder) super.setAnimatorProvider(animatorProvider); + } + + public Builder setDeathMaxRotation(float angle) { + this.deathMaxRotationProvider = $ -> angle; + return this; + } + + /** + * Sets a provider for the max rotation value for dying entities.
+ * You might want to modify this for different aesthetics, such as a + * {@link net.minecraft.world.entity.monster.Spider} flipping upside down on death.
+ * Functionally equivalent to {@link net.minecraft.client.renderer.entity.LivingEntityRenderer#getFlipDegrees} + */ + public Builder setDeathMaxRotation(Function deathMaxRotationProvider) { + this.deathMaxRotationProvider = deathMaxRotationProvider; + return this; + } + + @Override + public AzEntityRendererConfig build() { + var baseConfig = super.build(); + + return new AzEntityRendererConfig<>( + baseConfig::createAnimator, + deathMaxRotationProvider, + baseConfig::modelLocation, + baseConfig::getRenderType, + baseConfig.renderLayers(), + baseConfig::textureLocation, + baseConfig.scaleHeight(), + baseConfig.scaleWidth() + ); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java new file mode 100644 index 000000000..912664ff9 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java @@ -0,0 +1,113 @@ +package mod.azure.azurelib.rewrite.render.entity; + +import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.cache.texture.AnimatableTexture; +import mod.azure.azurelib.rewrite.render.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Mob; +import org.joml.Matrix4f; + +/** + * Represents a renderer pipeline specifically designed for rendering entities. This pipeline facilitates stages of + * rendering where contextual work like pre-translations, texture animations, and leash rendering are managed within a + * customizable structure. + * + * @param The type of entity this renderer pipeline handles. Extends from the base {@link Entity}. + */ +public class AzEntityRendererPipeline extends AzRendererPipeline { + + private final AzEntityRenderer entityRenderer; + + protected Matrix4f entityRenderTranslations = new Matrix4f(); + + protected Matrix4f modelRenderTranslations = new Matrix4f(); + + public AzEntityRendererPipeline(AzEntityRendererConfig config, AzEntityRenderer entityRenderer) { + super(config); + this.entityRenderer = entityRenderer; + } + + @Override + protected AzRendererPipelineContext createContext(AzRendererPipeline rendererPipeline) { + return new AzEntityRendererPipelineContext<>(this); + } + + @Override + protected AzModelRenderer createModelRenderer(AzLayerRenderer layerRenderer) { + return new AzEntityModelRenderer<>(this, layerRenderer); + } + + @Override + protected AzLayerRenderer createLayerRenderer(AzRendererConfig config) { + return new AzEntityLayerRenderer<>(config::renderLayers); + } + + /** + * Update the current frame of a {@link AnimatableTexture potentially animated} texture used by this + * GeoRenderer.
+ * This should only be called immediately prior to rendering, and only + * + * @see AnimatableTexture#setAndUpdate(ResourceLocation, int) + */ + @Override + public void updateAnimatedTextureFrame(T entity) { + AnimatableTexture.setAndUpdate( + config.textureLocation(entity), + entity.getId() + entity.tickCount + ); + } + + /** + * Called before rendering the model to buffer. Allows for render modifications and preparatory work such as scaling + * and translating.
+ * {@link PoseStack} translations made here are kept until the end of the render process + */ + @Override + public void preRender(AzRendererPipelineContext context, boolean isReRender) { + var poseStack = context.poseStack(); + this.entityRenderTranslations.set(poseStack.last().pose()); + + var config = entityRenderer.config(); + var scaleWidth = config.scaleWidth(); + var scaleHeight = config.scaleHeight(); + + scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); + } + + @Override + public void postRender(AzRendererPipelineContext context, boolean isReRender) {} + + /** + * Renders the final frame of the entity, including handling special cases such as entities with leashes. + * + * @param context the rendering context that contains all required data for rendering, such as the entity, pose + * stack, light information, and buffer source + */ + @Override + public void renderFinal(AzRendererPipelineContext context) { + var bufferSource = context.multiBufferSource(); + var entity = context.animatable(); + var packedLight = context.packedLight(); + var partialTick = context.partialTick(); + var poseStack = context.poseStack(); + + entityRenderer.superRender(entity, 0, partialTick, poseStack, bufferSource, packedLight); + + if (!(entity instanceof Mob mob)) { + return; + } + + var leashHolder = mob.getLeashHolder(); + + if (leashHolder == null) { + return; + } + + AzEntityLeashRenderUtil.renderLeash(entityRenderer, mob, partialTick, poseStack, bufferSource, leashHolder); + } + + public AzEntityRenderer getRenderer() { + return entityRenderer; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipelineContext.java new file mode 100644 index 000000000..219502717 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipelineContext.java @@ -0,0 +1,53 @@ +package mod.azure.azurelib.rewrite.render.entity; + +import mod.azure.azurelib.rewrite.render.AzRendererPipeline; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A context class specifically for rendering entities using a custom rendering pipeline. This class extends + * {@code AzRendererPipelineContext} and provides implementations for methods to customize entity rendering, such as + * determining default render types and packed overlay settings. + * + * @param the type of entity being rendered, extending {@code Entity} + */ +public class AzEntityRendererPipelineContext extends AzRendererPipelineContext { + + public AzEntityRendererPipelineContext(AzRendererPipeline rendererPipeline) { + super(rendererPipeline); + } + + @Override + public @NotNull RenderType getDefaultRenderType( + T animatable, + ResourceLocation texture, + @Nullable MultiBufferSource bufferSource, + float partialTick + ) { + return RenderType.entityCutoutNoCull(texture); + } + + /** + * Gets a packed overlay coordinate pair for rendering.
+ * Mostly just used for the red tint when an entity is hurt, but can be used for other things like the + * {@link net.minecraft.world.entity.monster.Creeper} white tint when exploding. + */ + @Override + public int getPackedOverlay(T entity, float u, float partialTick) { + if (!(entity instanceof LivingEntity livingEntity)) { + return OverlayTexture.NO_OVERLAY; + } + + return OverlayTexture.pack( + OverlayTexture.u(u), + OverlayTexture.v(livingEntity.hurtTime > 0 || livingEntity.deathTime > 0) + ); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemGuiRenderUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemGuiRenderUtil.java new file mode 100644 index 000000000..3f39dd0e3 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemGuiRenderUtil.java @@ -0,0 +1,56 @@ +package mod.azure.azurelib.rewrite.render.item; + +import com.mojang.blaze3d.platform.Lighting; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.world.item.ItemStack; + +public class AzItemGuiRenderUtil { + + /** + * Wrapper method to handle rendering the item in a GUI context (defined by + * {@link net.minecraft.world.item.ItemDisplayContext#GUI} normally).
+ * Just includes some additional required transformations and settings. + */ + public static void renderInGui( + AzItemRendererConfig config, + AzItemRendererPipeline rendererPipeline, + ItemStack stack, + AzBakedModel model, + ItemStack currentItemStack, + PoseStack poseStack, + MultiBufferSource source, + int packedLight + ) { + if (config.useEntityGuiLighting()) { + Lighting.setupForEntityInInventory(); + } else { + Lighting.setupForFlatItems(); + } + + var partialTick = Minecraft.getInstance().getFrameTime(); + var bSource = + source instanceof MultiBufferSource.BufferSource bufferSource + ? bufferSource + : Minecraft.getInstance().levelRenderer.renderBuffers.bufferSource(); + var textureLocation = config.textureLocation(stack); + var renderType = rendererPipeline.context() + .getDefaultRenderType(stack, textureLocation, bSource, partialTick); + var withGlint = currentItemStack != null && currentItemStack.hasFoil(); + var buffer = ItemRenderer.getFoilBufferDirect(source, renderType, true, withGlint); + + poseStack.pushPose(); + + rendererPipeline.render(poseStack, model, stack, bSource, renderType, buffer, 0, partialTick, packedLight, 1, 1, 1, 1); + + bSource.endBatch(); + RenderSystem.enableDepthTest(); + Lighting.setupFor3DItems(); + + poseStack.popPose(); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemModelRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemModelRenderer.java new file mode 100644 index 000000000..744ba6375 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemModelRenderer.java @@ -0,0 +1,77 @@ +package mod.azure.azurelib.rewrite.render.item; + +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.AzLayerRenderer; +import mod.azure.azurelib.rewrite.render.AzModelRenderer; +import mod.azure.azurelib.rewrite.render.AzPhasedRenderer; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; + +/** + * AzItemModelRenderer is a specialized implementation of {@link AzModelRenderer} for rendering {@link ItemStack} + * objects. It provides customized rendering logic for rendering item models in a layered and recursive manner. + */ +public class AzItemModelRenderer extends AzModelRenderer { + + private final AzItemRendererPipeline itemRendererPipeline; + + public AzItemModelRenderer(AzItemRendererPipeline itemRendererPipeline, AzLayerRenderer layerRenderer) { + super(itemRendererPipeline, layerRenderer); + this.itemRendererPipeline = itemRendererPipeline; + } + + /** + * The actual render method that subtype renderers should override to handle their specific rendering tasks.
+ * {@link AzPhasedRenderer#preRender} has already been called by this stage, and {@link AzPhasedRenderer#postRender} + * will be called directly after + */ + @Override + public void render(AzRendererPipelineContext context, boolean isReRender) { + if (!isReRender) { + var animatable = context.animatable(); + var animator = itemRendererPipeline.getRenderer().getAnimator(); + + if (animator != null) { + animator.animate(animatable, context.partialTick()); + } + } + + var poseStack = context.poseStack(); + + itemRendererPipeline.modelRenderTranslations = new Matrix4f(poseStack.last().pose()); + + super.render(context, isReRender); + } + + /** + * Renders the provided {@link AzBone} and its associated child bones + */ + @Override + public void renderRecursively(AzRendererPipelineContext context, AzBone bone, boolean isReRender) { + if (bone.isTrackingMatrices()) { + var animatable = context.animatable(); + var poseStack = context.poseStack(); + var poseState = new Matrix4f(poseStack.last().pose()); + var localMatrix = RenderUtils.invertAndMultiplyMatrices( + poseState, + itemRendererPipeline.itemRenderTranslations + ); + + bone.setModelSpaceMatrix( + RenderUtils.invertAndMultiplyMatrices(poseState, itemRendererPipeline.modelRenderTranslations) + ); + bone.setLocalSpaceMatrix( + RenderUtils.translateMatrix(localMatrix, getRenderOffset(animatable, 1).toVector3f()) + ); + } + + super.renderRecursively(context, bone, isReRender); + } + + public Vec3 getRenderOffset(ItemStack itemStack, float f) { + return Vec3.ZERO; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java new file mode 100644 index 000000000..daa8c8025 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java @@ -0,0 +1,96 @@ +package mod.azure.azurelib.rewrite.render.item; + +import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.render.AzProvider; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * AzItemRenderer is an abstract base class for rendering custom animated items in a game framework. It provides + * utilities for handling item models, textures, and animations via a configurable pipeline and provider system. This + * class supports rendering of items both in GUI contexts and in-world as entities, enabling advanced visual effects + * such as custom animations and lighting.
+ * The rendering process utilizes a pipeline to manage render layers, textures, and baked models, integrating with game + * frame components like PoseStack and MultiBufferSource. + */ +public abstract class AzItemRenderer { + + private final AzItemRendererConfig config; + + private final AzProvider provider; + + private final AzItemRendererPipeline rendererPipeline; + + @Nullable + private AzItemAnimator reusedAzItemAnimator; + + protected AzItemRenderer( + AzItemRendererConfig config + ) { + this.rendererPipeline = createPipeline(config); + this.provider = new AzProvider<>(config::createAnimator, config::modelLocation); + this.config = config; + } + + protected AzItemRendererPipeline createPipeline(AzItemRendererConfig config) { + return new AzItemRendererPipeline(config, this); + } + + public void renderByGui( + ItemStack stack, + @NotNull PoseStack poseStack, + @NotNull MultiBufferSource source, + int packedLight + ) { + var model = provider.provideBakedModel(stack); + + prepareAnimator(stack, model); + + AzItemGuiRenderUtil.renderInGui(config, rendererPipeline, stack, model, stack, poseStack, source, packedLight); + } + + public void renderByItem( + ItemStack stack, + @NotNull PoseStack poseStack, + @NotNull MultiBufferSource source, + int packedLight + ) { + var model = provider.provideBakedModel(stack); + var partialTick = Minecraft.getInstance().getFrameTime(); + var textureLocation = config.textureLocation(stack); + var renderType = rendererPipeline.context() + .getDefaultRenderType(stack, textureLocation, source, partialTick); + // TODO: Why the null check here? + var withGlint = stack != null && stack.hasFoil(); + var buffer = ItemRenderer.getFoilBufferDirect(source, renderType, false, withGlint); + + prepareAnimator(stack, model); + + rendererPipeline.render(poseStack, model, stack, source, renderType, buffer, 0, partialTick, packedLight, 1, 1, 1, 1); + } + + private void prepareAnimator(ItemStack stack, AzBakedModel model) { + var cachedEntityAnimator = (AzItemAnimator) provider.provideAnimator(stack); + + if (cachedEntityAnimator != null && model != null) { + cachedEntityAnimator.setActiveModel(model); + } + + // Point the renderer's current animator reference to the cached entity animator before rendering. + reusedAzItemAnimator = cachedEntityAnimator; + } + + public @Nullable AzItemAnimator getAnimator() { + return reusedAzItemAnimator; + } + + public AzItemRendererConfig config() { + return config; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java new file mode 100644 index 000000000..7f7e20240 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java @@ -0,0 +1,128 @@ +package mod.azure.azurelib.rewrite.render.item; + +import mod.azure.azurelib.rewrite.animation.AzAnimator; +import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Configuration class for rendering items using customized settings in an animation framework. Extends + * {@link AzRendererConfig} specifically for handling {@link ItemStack}. Provides additional settings specific to item + * rendering, such as GUI lighting and custom offsets. + */ +public class AzItemRendererConfig extends AzRendererConfig { + + private final boolean useEntityGuiLighting; + + private final boolean useNewOffset; + + private AzItemRendererConfig( + Supplier> animatorProvider, + Function modelLocationProvider, + Function renderTypeProvider, + List> renderLayers, + Function textureLocationProvider, + float scaleHeight, + float scaleWidth, + boolean useEntityGuiLighting, + boolean useNewOffset + ) { + super( + animatorProvider, + modelLocationProvider, + renderTypeProvider, + renderLayers, + textureLocationProvider, + scaleHeight, + scaleWidth + ); + this.useEntityGuiLighting = useEntityGuiLighting; + this.useNewOffset = useNewOffset; + } + + public boolean useEntityGuiLighting() { + return useEntityGuiLighting; + } + + public boolean useNewOffset() { + return useNewOffset; + } + + public static Builder builder( + ResourceLocation modelLocation, + ResourceLocation textureLocation + ) { + return new Builder($ -> modelLocation, $ -> textureLocation); + } + + public static Builder builder( + Function modelLocationProvider, + Function textureLocationProvider + ) { + return new Builder(modelLocationProvider, textureLocationProvider); + } + + public static class Builder extends AzRendererConfig.Builder { + + private boolean useEntityGuiLighting; + + private boolean useNewOffset; + + protected Builder( + Function modelLocationProvider, + Function textureLocationProvider + ) { + super(modelLocationProvider, textureLocationProvider); + this.useEntityGuiLighting = false; + this.useNewOffset = false; + } + + @Override + public Builder addRenderLayer(AzRenderLayer renderLayer) { + return (Builder) super.addRenderLayer(renderLayer); + } + + @Override + public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animatorProvider) { + return (Builder) super.setAnimatorProvider(animatorProvider); + } + + public Builder useEntityGuiLighting() { + this.useEntityGuiLighting = true; + return this; + } + + /** + * @param useNewOffset Determines whether to apply the y offset for a model due to the change in BlockBench + * 4.11. + */ + public AzRendererConfig.Builder useNewOffset(boolean useNewOffset) { + this.useNewOffset = useNewOffset; + return this; + } + + @Override + public AzItemRendererConfig build() { + var baseConfig = super.build(); + + return new AzItemRendererConfig( + baseConfig::createAnimator, + baseConfig::modelLocation, + baseConfig::getRenderType, + baseConfig.renderLayers(), + baseConfig::textureLocation, + baseConfig.scaleHeight(), + baseConfig.scaleWidth(), + useEntityGuiLighting, + useNewOffset + ); + } + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java new file mode 100644 index 000000000..9dee871ee --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java @@ -0,0 +1,90 @@ +package mod.azure.azurelib.rewrite.render.item; + +import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.cache.texture.AnimatableTexture; +import mod.azure.azurelib.rewrite.render.AzLayerRenderer; +import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import mod.azure.azurelib.rewrite.render.AzRendererPipeline; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.joml.Matrix4f; + +/** + * Extends the {@link AzRendererPipeline} to provide a specific implementation for rendering {@link ItemStack} objects. + * This pipeline includes methods and configurations designed for item rendering and leverages additional utilities such + * as translation matrices and scaling functionalities for accurate rendering. + */ +public class AzItemRendererPipeline extends AzRendererPipeline { + + private final AzItemRenderer itemRenderer; + + protected Matrix4f itemRenderTranslations = new Matrix4f(); + + protected Matrix4f modelRenderTranslations = new Matrix4f(); + + public AzItemRendererPipeline(AzItemRendererConfig config, AzItemRenderer itemRenderer) { + super(config); + this.itemRenderer = itemRenderer; + } + + @Override + protected AzRendererPipelineContext createContext(AzRendererPipeline rendererPipeline) { + return new AzItemRendererPipelineContext(rendererPipeline); + } + + @Override + protected AzItemModelRenderer createModelRenderer(AzLayerRenderer layerRenderer) { + return new AzItemModelRenderer(this, layerRenderer); + } + + @Override + protected AzLayerRenderer createLayerRenderer(AzRendererConfig config) { + return new AzLayerRenderer<>(config::renderLayers); + } + + /** + * Called before rendering the model to buffer. Allows for render modifications and preparatory work such as scaling + * and translating.
+ * {@link PoseStack} translations made here are kept until the end of the render process + */ + @Override + public void preRender(AzRendererPipelineContext context, boolean isReRender) { + var poseStack = context.poseStack(); + this.itemRenderTranslations = new Matrix4f(poseStack.last().pose()); + + var config = itemRenderer.config(); + var scaleWidth = config.scaleWidth(); + var scaleHeight = config.scaleHeight(); + scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); + + if (!isReRender) { + var useNewOffset = config.useNewOffset(); + poseStack.translate(0.5f, useNewOffset ? 0.0f : 0.51f, 0.5f); + } + } + + @Override + public void postRender(AzRendererPipelineContext context, boolean isReRender) {} + + /** + * Update the current frame of a {@link AnimatableTexture potentially animated} texture used by this + * GeoRenderer.
+ * This should only be called immediately prior to rendering, and only + * + * @see AnimatableTexture#setAndUpdate(ResourceLocation, int) + */ + @Override + public void updateAnimatedTextureFrame(ItemStack animatable) { + AnimatableTexture.setAndUpdate( + config.textureLocation(animatable), + Item.getId(animatable.getItem()) + (int) RenderUtils.getCurrentTick() + ); + } + + public AzItemRenderer getRenderer() { + return itemRenderer; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipelineContext.java new file mode 100644 index 000000000..2d418134e --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipelineContext.java @@ -0,0 +1,35 @@ +package mod.azure.azurelib.rewrite.render.item; + +import mod.azure.azurelib.rewrite.render.AzRendererPipeline; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A specialized subclass of {@link AzRendererPipelineContext} designed for rendering {@link ItemStack} objects. + * Provides the default rendering context and pipeline for rendering item models within a custom rendering framework. + *
+ * This context delegates rendering operations to its associated {@link AzRendererPipeline} while providing additional + * configuration and control over the rendering process of an {@link ItemStack}. + */ +public class AzItemRendererPipelineContext extends AzRendererPipelineContext { + + public AzItemRendererPipelineContext(AzRendererPipeline rendererPipeline) { + super(rendererPipeline); + } + + // TODO: This is what Geckolib does, but it feels wrong to have this render type getter for an ITEM... + @Override + public @NotNull RenderType getDefaultRenderType( + ItemStack animatable, + ResourceLocation texture, + @Nullable MultiBufferSource bufferSource, + float partialTick + ) { + return RenderType.entityCutoutNoCull(texture); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererRegistry.java new file mode 100644 index 000000000..1164ae072 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererRegistry.java @@ -0,0 +1,39 @@ +package mod.azure.azurelib.rewrite.render.item; + +import net.minecraft.world.item.Item; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * The AzItemRendererRegistry class manages the association between items and their renderers in the context of the + * AzureLib framework. It provides functionality to register and retrieve item renderers dynamically, ensuring that + * appropriate renderers can be applied to specific item types or instances. + */ +public class AzItemRendererRegistry { + + private static final Map ITEM_TO_RENDERER = new HashMap<>(); + + private static final Map> ITEM_TO_RENDERER_SUPPLIER = new HashMap<>(); + + public static void register(Item item, Supplier itemRendererSupplier) { + ITEM_TO_RENDERER_SUPPLIER.put(item, itemRendererSupplier); + } + + public static void register(Supplier itemRendererSupplier, Item item, Item... items) { + register(item, itemRendererSupplier); + + for (var otherItem : items) { + register(otherItem, itemRendererSupplier); + } + } + + public static @Nullable AzItemRenderer getOrNull(Item item) { + return ITEM_TO_RENDERER.computeIfAbsent(item, ($) -> { + var rendererSupplier = ITEM_TO_RENDERER_SUPPLIER.get(item); + return rendererSupplier == null ? null : rendererSupplier.get(); + }); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzArmorLayer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzArmorLayer.java new file mode 100644 index 000000000..03d632a8f --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzArmorLayer.java @@ -0,0 +1,414 @@ +package mod.azure.azurelib.rewrite.render.layer; + +import com.mojang.authlib.GameProfile; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRenderer; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.SkullBlockRenderer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.*; +import net.minecraft.world.level.block.AbstractSkullBlock; +import net.minecraft.world.level.block.entity.SkullBlockEntity; +import net.minecraft.world.level.entity.EntityAccess; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +/** + * Builtin class for handling dynamic armor rendering on AzureLib entities.
+ * Supports {@link ArmorItem Vanilla} armor models.
+ * Unlike a traditional armor renderer, this renderer renders per-bone, giving much more flexible armor rendering. + */ +public class AzArmorLayer implements AzRenderLayer { + + protected static final Map ARMOR_PATH_CACHE = new Object2ObjectOpenHashMap<>(); + + protected static final HumanoidModel INNER_ARMOR_MODEL = new HumanoidModel<>( + Minecraft.getInstance().getEntityModels().bakeLayer(ModelLayers.PLAYER_INNER_ARMOR) + ); + + protected static final HumanoidModel OUTER_ARMOR_MODEL = new HumanoidModel<>( + Minecraft.getInstance().getEntityModels().bakeLayer(ModelLayers.PLAYER_OUTER_ARMOR) + ); + + @Nullable + protected ItemStack mainHandStack; + + @Nullable + protected ItemStack offhandStack; + + @Nullable + protected ItemStack helmetStack; + + @Nullable + protected ItemStack chestplateStack; + + @Nullable + protected ItemStack leggingsStack; + + @Nullable + protected ItemStack bootsStack; + + /** + * Prepares the necessary item stacks for rendering by accessing the relevant equipment slots of the animatable + * instance. If the animatable instance is not a LivingEntity, the method returns without action. + * + * @param context The rendering context containing the animatable instance and other necessary data for rendering. + */ + @Override + public void preRender(AzRendererPipelineContext context) { + this.mainHandStack = context.animatable().getItemBySlot(EquipmentSlot.MAINHAND); + this.offhandStack = context.animatable().getItemBySlot(EquipmentSlot.OFFHAND); + this.helmetStack = context.animatable().getItemBySlot(EquipmentSlot.HEAD); + this.chestplateStack = context.animatable().getItemBySlot(EquipmentSlot.CHEST); + this.leggingsStack = context.animatable().getItemBySlot(EquipmentSlot.LEGS); + this.bootsStack = context.animatable().getItemBySlot(EquipmentSlot.FEET); + } + + @Override + public void render(AzRendererPipelineContext context) {} + + /** + * Renders the given armor or skull block for the specified bone using the provided rendering context. Depending on + * the type of item, it delegates rendering to appropriate methods. + * + * @param context The rendering context containing necessary parameters for rendering, like pose stack, light level, + * etc. + * @param bone The specific bone of the model where the armor or skull block will be rendered. + */ + @Override + public void renderForBone(AzRendererPipelineContext context, AzBone bone) { + var poseStack = context.poseStack(); + var armorStack = getArmorItemForBone(context, bone); + + if (armorStack == null) { + return; + } + + if ( + armorStack.getItem() instanceof BlockItem blockItem && blockItem + .getBlock() instanceof AbstractSkullBlock skullBlock + ) { + renderSkullAsArmor(context, bone, armorStack, skullBlock); + } else { + renderArmor(context, bone, armorStack, poseStack); + } + } + + /** + * Renders armor items on a given bone within the render cycle of a model. This method determines the appropriate + * equipment slot, renderer, and model for the armor item and handles the rendering process accordingly. + * + * @param context The rendering context containing the animatable instance and other data essential for + * rendering. + * @param bone The specific bone of the model where the armor piece will be rendered. + * @param armorStack The ItemStack representing the armor item to render. + * @param poseStack The matrix stack used to apply transformations during rendering. + */ + private void renderArmor( + AzRendererPipelineContext context, + AzBone bone, + ItemStack armorStack, + PoseStack poseStack + ) { + var slot = getEquipmentSlotForBone(context, bone, armorStack); + var renderer = getRendererForItem(armorStack); + var model = getModelForItem(armorStack, slot); + var modelPart = getModelPartForBone(context, model); + + if (!modelPart.cubes.isEmpty()) { + poseStack.pushPose(); + poseStack.scale(-1, -1, 1); + + if (renderer != null) { + var boneContext = renderer.rendererPipeline().context().boneContext(); + + prepModelPartForRender(context, bone, modelPart); + renderer.prepForRender(context.animatable(), armorStack, slot, model); + boneContext.applyBoneVisibilityByPart(slot, modelPart, model); + model.renderToBuffer( + poseStack, + null, + context.packedLight(), + context.packedOverlay(), + context.red(), + context.green(), + context.blue(), + context.alpha() + ); + } else if (armorStack.getItem() instanceof ArmorItem) { + prepModelPartForRender(context, bone, modelPart); + renderVanillaArmorPiece( + context, + bone, + slot, + armorStack, + modelPart + ); + } + + poseStack.popPose(); + } + } + + /** + * Return an EquipmentSlot for a given {@link ItemStack} and animatable instance.
+ * This is what determines the base model to use for rendering a particular stack + */ + protected @NotNull EquipmentSlot getEquipmentSlotForBone( + AzRendererPipelineContext context, + AzBone bone, + ItemStack stack + ) { + var animatable = context.animatable(); + + for (var slot : EquipmentSlot.values()) { + var isHumanoidArmorSlotType = slot.getType() == EquipmentSlot.Type.ARMOR; + + if (isHumanoidArmorSlotType && stack == animatable.getItemBySlot(slot)) { + return slot; + } + } + + return EquipmentSlot.CHEST; + } + + /** + * Return a ModelPart for a given {@link AzBone}.
+ * This is then transformed into position for the final render + */ + @NotNull + protected ModelPart getModelPartForBone(AzRendererPipelineContext context, HumanoidModel baseModel) { + return baseModel.body; + } + + /** + * Get the {@link ItemStack} relevant to the bone being rendered.
+ * Return null if this bone should be ignored + */ + @Nullable + protected ItemStack getArmorItemForBone(AzRendererPipelineContext context, AzBone bone) { + return null; + } + + /** + * Renders an individual armor piece base on the given {@link AzBone} and {@link ItemStack} + */ + protected void renderVanillaArmorPiece( + AzRendererPipelineContext context, + AzBone bone, + EquipmentSlot slot, + ItemStack armorStack, + ModelPart modelPart + ) { + var buffer = getVanillaArmorBuffer( + context, + armorStack, + slot, + bone, + false + ); + + modelPart.render(context.poseStack(), buffer, context.packedLight(), context.packedOverlay()); + + if (armorStack.hasFoil()) + modelPart.render( + context.poseStack(), + getVanillaArmorBuffer( + context, + armorStack, + slot, + bone, + true + ), + context.packedLight(), + context.packedOverlay(), + context.red(), + context.green(), + context.blue(), + context.alpha() + ); + } + + /** + * Retrieves a {@link VertexConsumer} for rendering vanilla-styled armor. The method determines whether the armor + * should apply a glint effect or not and selects the appropriate render type accordingly. + * + * @param context The rendering context providing necessary data for rendering, including the animatable instance + * and the buffer source. + * @param stack The armor {@link ItemStack} being rendered. + * @param slot The {@link EquipmentSlot} the armor piece occupies. + * @param bone The model bone associated with the armor piece. + * @param forGlint A flag indicating whether the armor piece should render with a glint effect. + * @return The {@link VertexConsumer} used to render the designated armor piece with the appropriate style and + * effect. + */ + protected VertexConsumer getVanillaArmorBuffer( + AzRendererPipelineContext context, + ItemStack stack, + EquipmentSlot slot, + AzBone bone, + boolean forGlint + ) { + if (forGlint) { + return context.multiBufferSource().getBuffer(RenderType.armorEntityGlint()); + } + + return context.multiBufferSource() + .getBuffer(RenderType.armorCutoutNoCull(getVanillaArmorResource(context.animatable(), stack, slot, bone.getName()))); + } + + /** + * Retrieves the appropriate {@link AzArmorRenderer} for the given {@link ItemStack}. This method uses the + * {@link AzArmorRendererRegistry} to fetch a renderer if one is registered for the specified item's class or + * instance. + * + * @param stack The {@link ItemStack} for which the renderer is to be obtained. + * @return The {@link AzArmorRenderer} associated with the item in the stack, or null if no renderer exists. + */ + protected @Nullable AzArmorRenderer getRendererForItem(ItemStack stack) { + var item = stack.getItem(); + return AzArmorRendererRegistry.getOrNull(item); + } + + /** + * Returns a cached instance of a base HumanoidModel that is used for rendering/modelling the provided + * {@link ItemStack} + */ + protected HumanoidModel getModelForItem(ItemStack stack, EquipmentSlot slot) { + var renderer = getRendererForItem(stack); + + if (renderer == null) { + return slot == EquipmentSlot.LEGS ? INNER_ARMOR_MODEL : OUTER_ARMOR_MODEL; + } + + return renderer.rendererPipeline().armorModel(); + } + + /** + * Render a given {@link AbstractSkullBlock} as a worn armor piece in relation to a given {@link AzBone} + */ + protected void renderSkullAsArmor( + AzRendererPipelineContext context, + AzBone bone, + ItemStack stack, + AbstractSkullBlock skullBlock + ) { + GameProfile skullProfile = null; + CompoundTag stackTag = stack.getTag(); + + if (stackTag != null) { + Tag skullTag = stackTag.get(PlayerHeadItem.TAG_SKULL_OWNER); + + if (skullTag instanceof CompoundTag compoundTag) { + skullProfile = NbtUtils.readGameProfile(compoundTag); + } + else if (skullTag instanceof StringTag tag) { + String skullOwner = tag.getAsString(); + + if (!skullOwner.isBlank()) { + CompoundTag profileTag = new CompoundTag(); + + SkullBlockEntity.updateGameprofile(new GameProfile(null, skullOwner), name -> + stackTag.put(PlayerHeadItem.TAG_SKULL_OWNER, NbtUtils.writeGameProfile(profileTag, name))); + + skullProfile = NbtUtils.readGameProfile(profileTag); + } + } + } + var type = skullBlock.getType(); + var model = SkullBlockRenderer.createSkullRenderers(Minecraft.getInstance().getEntityModels()) + .get(type); + var renderType = SkullBlockRenderer.getRenderType(type, skullProfile); + + context.poseStack().pushPose(); + RenderUtils.translateAndRotateMatrixForBone(context.poseStack(), bone); + context.poseStack().scale(1.1875f, 1.1875f, 1.1875f); + context.poseStack().translate(-0.5f, 0, -0.5f); + SkullBlockRenderer.renderSkull( + null, + 0, + 0, + context.poseStack(), + context.multiBufferSource(), + context.packedLight(), + model, + renderType + ); + context.poseStack().popPose(); + } + + /** + * Prepares the given {@link ModelPart} for render by setting its translation, position, and rotation values based + * on the provided {@link AzBone} + * + * @param context + * @param bone The AzBone to base the translations on + * @param sourcePart The ModelPart to translate + */ + protected void prepModelPartForRender(AzRendererPipelineContext context, AzBone bone, ModelPart sourcePart) { + var firstCube = bone.getCubes().get(0); + var armorCube = sourcePart.cubes.get(0); + var armorBoneSizeX = firstCube.size().x(); + var armorBoneSizeY = firstCube.size().y(); + var armorBoneSizeZ = firstCube.size().z(); + var actualArmorSizeX = Math.abs(armorCube.maxX - armorCube.minX); + var actualArmorSizeY = Math.abs(armorCube.maxY - armorCube.minY); + var actualArmorSizeZ = Math.abs(armorCube.maxZ - armorCube.minZ); + var scaleX = (float) (armorBoneSizeX / actualArmorSizeX); + var scaleY = (float) (armorBoneSizeY / actualArmorSizeY); + var scaleZ = (float) (armorBoneSizeZ / actualArmorSizeZ); + + sourcePart.setPos( + -(bone.getPivotX() - ((bone.getPivotX() * scaleX) - bone.getPivotX()) / scaleX), + -(bone.getPivotY() - ((bone.getPivotY() * scaleY) - bone.getPivotY()) / scaleY), + (bone.getPivotZ() - ((bone.getPivotZ() * scaleZ) - bone.getPivotZ()) / scaleZ) + ); + + sourcePart.xRot = -bone.getRotX(); + sourcePart.yRot = -bone.getRotY(); + sourcePart.zRot = bone.getRotZ(); + + context.poseStack().scale(scaleX, scaleY, scaleZ); + } + + /** + * Gets a cached resource path for the vanilla armor layer texture for this armor piece. + *

+ * Equivalent to {@link net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer#getArmorLocation HumanoidArmorLayer.getArmorLocation} + */ + public ResourceLocation getVanillaArmorResource(Entity entity, ItemStack stack, EquipmentSlot slot, String type) { + String domain = "minecraft"; + String path = ((ArmorItem) stack.getItem()).getMaterial().getName(); + String[] materialNameSplit = path.split(":", 2); + + if (materialNameSplit.length > 1) { + domain = materialNameSplit[0]; + path = materialNameSplit[1]; + } + + if (!type.isBlank()) + type = "_" + type; + + String texture = String.format("%s:textures/models/armor/%s_layer_%d%s.png", domain, path, (slot == EquipmentSlot.LEGS ? 2 : 1), type); + return ARMOR_PATH_CACHE.computeIfAbsent(texture, ResourceLocation::new); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzAutoGlowingLayer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzAutoGlowingLayer.java new file mode 100644 index 000000000..97f5e081e --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzAutoGlowingLayer.java @@ -0,0 +1,55 @@ +package mod.azure.azurelib.rewrite.render.layer; + +import mod.azure.azurelib.cache.texture.AutoGlowingTexture; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import net.minecraft.client.renderer.RenderType; + +/** + * A {@link AzRenderLayer} dedicated to rendering the auto-generated glow layer functionality provided by AzureLib. This + * utilizes texture files with the _glowing suffix to create glowing effects for models. + */ +public class AzAutoGlowingLayer implements AzRenderLayer { + + @Override + public void preRender(AzRendererPipelineContext context) {} + + /** + * Handles the main rendering logic for the animatable object in the pipeline context. This includes switching to a + * custom {@link RenderType} for glowing textures and rendering the object using the pipeline's re-render mechanism. + *

+ * The rendering context's state is modified temporarily to apply a custom render type and packed light. After + * rendering, the context is restored to its original state for consistency. + *

+ * + * @param context the rendering pipeline context, containing the animatable object and rendering state + */ + @Override + public void render(AzRendererPipelineContext context) { + var animatable = context.animatable(); + var renderPipeline = context.rendererPipeline(); + var textureLocation = renderPipeline.config().textureLocation(animatable); + var renderType = AutoGlowingTexture.getRenderType(textureLocation); + + if (context.renderType() != null) { + var prevRenderType = context.renderType(); + var prevPackedLight = context.packedLight(); + var prevVertexConsumer = context.vertexConsumer(); + + context.setRenderType(renderType); + context.setPackedLight(0xF00000); + context.setVertexConsumer(context.multiBufferSource().getBuffer(renderType)); + + renderPipeline.reRender(context); + + // Restore context for sanity + // TODO: Should probably cache the context as a whole somewhere and then restore it (a "previous" context). + context.setRenderType(prevRenderType); + context.setPackedLight(prevPackedLight); + context.setVertexConsumer(prevVertexConsumer); + } + } + + @Override + public void renderForBone(AzRendererPipelineContext context, AzBone bone) {} +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzBlockAndItemLayer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzBlockAndItemLayer.java new file mode 100644 index 000000000..4cf7ed782 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzBlockAndItemLayer.java @@ -0,0 +1,179 @@ +package mod.azure.azurelib.rewrite.render.layer; + +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.AzRendererPipeline; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.util.RenderUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.function.Function; + +/** + * A {@link AzRenderLayer} responsible for rendering {@link BlockState + * BlockStates} or {@link ItemStack ItemStacks} onto a specified {@link AzRendererPipeline}. + * This layer handles the rendering of physical elements, such as blocks and items, associated with animation bones. + */ +public class AzBlockAndItemLayer implements AzRenderLayer { + + protected final Function itemStackProvider; + + protected final Function blockStateProvider; + + public AzBlockAndItemLayer() { + this(bone -> null, bone -> null); + } + + public AzBlockAndItemLayer( + Function itemStackProvider, + Function blockStateProvider + ) { + super(); + + this.itemStackProvider = itemStackProvider; + this.blockStateProvider = blockStateProvider; + } + + @Override + public void preRender(AzRendererPipelineContext context) {} + + @Override + public void render(AzRendererPipelineContext context) {} + + /** + * Renders an {@link ItemStack} or {@link BlockState} associated with the specified bone in the rendering context. + * If both the {@link ItemStack} and {@link BlockState} are {@code null}, no rendering occurs. + *

+ * This method applies the bone's transformations to the current rendering matrix stack before rendering, ensuring + * the item or block appears correctly positioned and oriented relative to the bone. + *

+ * + * @param context the rendering pipeline context, containing rendering state and utilities + * @param bone the bone for which to render associated elements + */ + @Override + public void renderForBone(AzRendererPipelineContext context, AzBone bone) { + var stack = itemStackForBone(bone); + var blockState = blockStateForBone(bone); + + if (stack == null && blockState == null) + return; + + context.poseStack().pushPose(); + RenderUtils.translateAndRotateMatrixForBone(context.poseStack(), bone); + + if (stack != null) + renderItemForBone(context, bone, stack); + + if (blockState != null) + renderBlockForBone(context, bone, blockState); + + context.poseStack().popPose(); + } + + /** + * Retrieves the {@link ItemStack} associated with the given bone for rendering purposes. Returns {@code null} if + * there is no {@link ItemStack} to render for this bone. + * + * @param bone the bone for which to retrieve the {@link ItemStack} + * @return the {@link ItemStack} relevant to the specified bone, or {@code null} if none exists + */ + public ItemStack itemStackForBone(AzBone bone) { + return itemStackProvider.apply(bone); + } + + /** + * Retrieves the {@link BlockState} associated with the given bone for rendering purposes. Returns {@code null} if + * there is no {@link BlockState} to render for this bone. + * + * @param bone the bone for which to retrieve the {@link BlockState} + * @return the {@link BlockState} relevant to the specified bone, or {@code null} if none exists + */ + public BlockState blockStateForBone(AzBone bone) { + return blockStateProvider.apply(bone); + } + + /** + * Determines the specific {@link ItemDisplayContext} to use for rendering the given {@link ItemStack} on the + * specified bone. By default, this method returns {@link ItemDisplayContext#NONE}. + * + * @param bone the bone where the {@link ItemStack} will be rendered + * @param stack the {@link ItemStack} to render + * @return the {@link ItemDisplayContext} to use for rendering + */ + protected ItemDisplayContext getTransformTypeForStack(AzBone bone, ItemStack stack) { + return ItemDisplayContext.NONE; + } + + /** + * Renders the given {@link ItemStack} for the specified bone in the rendering context. The rendering adjusts based + * on whether the animatable object is a {@link LivingEntity}. + * + * @param context the rendering pipeline context + * @param bone the bone where the {@link ItemStack} will be rendered + * @param itemStack the {@link ItemStack} to render + */ + protected void renderItemForBone(AzRendererPipelineContext context, AzBone bone, ItemStack itemStack) { + if (context.animatable() instanceof LivingEntity livingEntity) { + Minecraft.getInstance() + .getItemRenderer() + .renderStatic( + livingEntity, + itemStack, + getTransformTypeForStack(bone, itemStack), + false, + context.poseStack(), + context.multiBufferSource(), + livingEntity.level(), + context.packedLight(), + context.packedOverlay(), + livingEntity.getId() + ); + } else { + Minecraft.getInstance() + .getItemRenderer() + .renderStatic( + itemStack, + getTransformTypeForStack(bone, itemStack), + context.packedLight(), + context.packedOverlay(), + context.poseStack(), + context.multiBufferSource(), + Minecraft.getInstance().level, + context.animatable().hashCode() + ); + } + } + + /** + * Renders the given {@link BlockState} for the specified bone in the rendering context. The block is rendered with + * adjusted position and scale to fit within the bone's space. + * + * @param context the rendering pipeline context + * @param bone the bone where the {@link BlockState} will be rendered + * @param blockState the {@link BlockState} to render + */ + protected void renderBlockForBone(AzRendererPipelineContext context, AzBone bone, BlockState blockState) { + context.poseStack().pushPose(); + + context.poseStack().translate(-0.25f, -0.25f, -0.25f); + context.poseStack().scale(0.5f, 0.5f, 0.5f); + + Minecraft.getInstance() + .getBlockRenderer() + .renderSingleBlock( + blockState, + context.poseStack(), + context.multiBufferSource(), + context.packedLight(), + OverlayTexture.NO_OVERLAY + ); + + context.poseStack().popPose(); + } + +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzRenderLayer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzRenderLayer.java new file mode 100644 index 000000000..a6305b1c7 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzRenderLayer.java @@ -0,0 +1,41 @@ +package mod.azure.azurelib.rewrite.render.layer; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.AzRendererPipeline; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import net.minecraft.client.renderer.MultiBufferSource; + +/** + * Render layer base class for rendering additional layers of effects or textures over an existing model at runtime.
+ * Contains the base boilerplate and helper code for various render layer features + */ +public interface AzRenderLayer { + + /** + * This method is called by the {@link AzRendererPipeline} before rendering, immediately after + * {@link AzRendererPipeline#preRender} has been called.
+ * This allows for RenderLayers to perform pre-render manipulations such as hiding or showing bones + */ + void preRender(AzRendererPipelineContext context); + + /** + * This is the method that is actually called by the render for your render layer to function.
+ * This is called after the animatable has been rendered, but before supplementary rendering like nametags. + */ + void render(AzRendererPipelineContext context); + + /** + * This method is called by the {@link AzRendererPipeline} for each bone being rendered.
+ * This is a more expensive call, particularly if being used to render something on a different buffer.
+ * It does however have the benefit of having the matrix translations and other transformations already applied from + * render-time.
+ * It's recommended to avoid using this unless necessary.
+ *
+ * The {@link AzBone} in question has already been rendered by this stage.
+ *
+ * If you do use it, and you render something that changes the {@link VertexConsumer buffer}, you need to + * reset it back to the previous buffer using {@link MultiBufferSource#getBuffer} before ending the method + */ + void renderForBone(AzRendererPipelineContext context, AzBone bone); +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/util/MoveAnalysis.java b/common/src/main/java/mod/azure/azurelib/rewrite/util/MoveAnalysis.java new file mode 100644 index 000000000..0962385c1 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/util/MoveAnalysis.java @@ -0,0 +1,123 @@ +package mod.azure.azurelib.rewrite.util; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; + +/** + * A utility class for analyzing the movement of an {@link Entity} in the game world. + *

+ * This class tracks the movement of an entity by calculating its position deltas + * over time. It provides methods to check if the entity is moving horizontally, + * vertically, or overall in any direction. + *

+ */ +public class MoveAnalysis { + + /** + * The {@link Entity} whose movement is being analyzed. + */ + private final Entity entity; + + /** + * The tick count of the last update. Used to avoid redundant updates. + */ + private int lastTick; + + /** + * The position of the entity at the last update. + */ + private Vec3 lastPosition; + + /** + * The change in the entity’s X-coordinate since the last update. + */ + private double deltaX; + + /** + * The change in the entity’s Y-coordinate since the last update. + */ + private double deltaY; + + /** + * The change in the entity’s Z-coordinate since the last update. + */ + private double deltaZ; + + /** + * Constructs a new {@code MoveAnalysis} instance for the specified entity. + * + * @param entity The {@link Entity} to analyze. + */ + public MoveAnalysis(Entity entity) { + this.entity = entity; + this.lastPosition = entity.position(); + } + + /** + * Updates the movement analysis with the entity's current position. + *

+ * This method calculates the change in position (delta) for each axis (X, Y, Z) + * since the last update. The update only occurs if the entity's tick count has + * increased since the last recorded tick. + *

+ */ + public void update() { + if (entity.tickCount == lastTick) { + // Only update on tick differences. + return; + } + + var prevPos = lastPosition; + var prevPosX = prevPos.x; + var prevPosY = prevPos.y; + var prevPosZ = prevPos.z; + + var pos = entity.position(); + var posX = pos.x; + var posY = pos.y; + var posZ = pos.z; + + this.deltaX = posX - prevPosX; + this.deltaY = posY - prevPosY; + this.deltaZ = posZ - prevPosZ; + + this.lastPosition = entity.position(); + this.lastTick = entity.tickCount; + } + + /** + * Checks if the entity is moving horizontally. + *

+ * Horizontal movement is defined as any change in the X or Z coordinates. + *

+ * + * @return {@code true} if the entity is moving horizontally; {@code false} otherwise. + */ + public boolean isMovingHorizontally() { + return deltaX != 0 || deltaZ != 0; + } + + /** + * Checks if the entity is moving vertically. + *

+ * Vertical movement is defined as any change in the Y coordinate. + *

+ * + * @return {@code true} if the entity is moving vertically; {@code false} otherwise. + */ + public boolean isMovingVertically() { + return deltaY != 0; + } + + /** + * Checks if the entity is moving in any direction. + *

+ * This includes both horizontal and vertical movement. + *

+ * + * @return {@code true} if the entity is moving; {@code false} otherwise. + */ + public boolean isMoving() { + return isMovingHorizontally() || isMovingVertically(); + } +} \ No newline at end of file diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/util/state/State.java b/common/src/main/java/mod/azure/azurelib/rewrite/util/state/State.java new file mode 100644 index 000000000..14a197244 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/util/state/State.java @@ -0,0 +1,21 @@ +package mod.azure.azurelib.rewrite.util.state; + +/** + * Represents a state within a state machine. A state defines the behavior that occurs when entering, updating, and + * exiting the state. The lifecycle of a state consists of three main methods:
+ *
    + *
  1. onEnter - Triggered when the state is entered, providing an opportunity to perform initialization.
  2. + *
  3. onUpdate - Called during state execution, typically to update or process logic related to the state.
  4. + *
  5. onExit - Triggered when transitioning out of the state, used for cleanup or finalization.
  6. + *
+ * + * @param the type of the context associated with the state, which must extend {@link StateMachineContext} + */ +public interface State { + + void onEnter(C context); + + void onUpdate(C context); + + void onExit(C context); +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachine.java b/common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachine.java new file mode 100644 index 000000000..b88b8f0fa --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachine.java @@ -0,0 +1,40 @@ +package mod.azure.azurelib.rewrite.util.state; + +/** + * Represents a state machine that handles transitions between different states. A state machine maintains a current + * state and allows transitions to new states, invoking lifecycle methods on each state during transitions. + * + * @param the type of the context associated with the state machine, which extends {@link StateMachineContext} + * @param the type of the states managed by the state machine, which extends {@link State} + */ +public abstract class StateMachine> { + + private final C reusableContext; + + private T state; + + public StateMachine(T initialState) { + this.state = initialState; + this.reusableContext = createContext(); + } + + protected abstract C createContext(); + + public void update(C context) { + state.onUpdate(context); + } + + public C getContext() { + return reusableContext; + } + + public T getState() { + return state; + } + + public void setState(T newState) { + state.onExit(reusableContext); + this.state = newState; + newState.onEnter(reusableContext); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachineContext.java new file mode 100644 index 000000000..5d388151c --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachineContext.java @@ -0,0 +1,14 @@ +package mod.azure.azurelib.rewrite.util.state; + +/** + * Represents the contract for a state machine context. A state machine context serves as an intermediary object that + * provides necessary data and functionality to state machine components or processes. It is generally used to + * encapsulate and manage shared resources, state-related details, and other dependencies required during the lifecycle + * of state transitions within a state machine.
+ * Implementations of this interface can define custom context-specific properties and methods, tailored to the + * requirements of a specific state machine.
+ * The context is typically associated with the {@link State} and {@link StateMachine} interfaces. Implementing classes + * provide relevant properties or methods that a state or state machine would require during its lifecycle operations + * (e.g., onEnter, onUpdate, onExit for states). + */ +public interface StateMachineContext {} diff --git a/common/src/main/java/mod/azure/azurelib/util/AzureLibUtil.java b/common/src/main/java/mod/azure/azurelib/util/AzureLibUtil.java index 7f96cab85..1a71fe10b 100644 --- a/common/src/main/java/mod/azure/azurelib/util/AzureLibUtil.java +++ b/common/src/main/java/mod/azure/azurelib/util/AzureLibUtil.java @@ -29,11 +29,17 @@ * Helper class for various AzureLib-specific functions. */ public final class AzureLibUtil { + + public static T self(Object object) { + return (T) object; + } + /** * Creates a new AnimatableInstanceCache for the given animatable object * * @param animatable The animatable object */ + @Deprecated(forRemoval = true) public static AnimatableInstanceCache createInstanceCache(GeoAnimatable animatable) { AnimatableInstanceCache cache = animatable.animatableCacheOverride(); @@ -47,6 +53,7 @@ public static AnimatableInstanceCache createInstanceCache(GeoAnimatable animatab * @param animatable The animatable object * @param singletonObject Whether the object is a singleton/flyweight object, and uses ints to differentiate animatable instances */ + @Deprecated(forRemoval = true) public static AnimatableInstanceCache createInstanceCache(GeoAnimatable animatable, boolean singletonObject) { AnimatableInstanceCache cache = animatable.animatableCacheOverride(); @@ -63,6 +70,7 @@ public static AnimatableInstanceCache createInstanceCache(GeoAnimatable animatab * @param name The name of the {@code LoopType} handler * @param loopType The {@code LoopType} implementation to use for the given name */ + @Deprecated(forRemoval = true) synchronized public static Animation.LoopType addCustomLoopType(String name, Animation.LoopType loopType) { return Animation.LoopType.register(name, loopType); } @@ -74,6 +82,7 @@ synchronized public static Animation.LoopType addCustomLoopType(String name, Ani * @param name The name of the {@code EasingType} handler * @param easingType The {@code EasingType} implementation to use for the given name */ + @Deprecated(forRemoval = true) synchronized public static EasingType addCustomEasingType(String name, EasingType easingType) { return EasingType.register(name, easingType); } @@ -85,6 +94,7 @@ synchronized public static EasingType addCustomEasingType(String name, EasingTyp * @param namespace The namespace (modid) to register the factory for * @param factory The factory responsible for model loading under the given namespace */ + @Deprecated(forRemoval = true) synchronized public static void addCustomBakedModelFactory(String namespace, BakedModelFactory factory) { BakedModelFactory.register(namespace, factory); } @@ -96,6 +106,7 @@ synchronized public static void addCustomBakedModelFactory(String namespace, Bak * @param dataTicket The SerializableDataTicket to register * @return The dataTicket you passed in */ + @Deprecated(forRemoval = true) synchronized public static SerializableDataTicket addDataTicket(SerializableDataTicket dataTicket) { return DataTickets.registerSerializable(dataTicket); } diff --git a/common/src/main/java/mod/azure/azurelib/util/RenderUtils.java b/common/src/main/java/mod/azure/azurelib/util/RenderUtils.java index eba3f5df4..85913d0b0 100644 --- a/common/src/main/java/mod/azure/azurelib/util/RenderUtils.java +++ b/common/src/main/java/mod/azure/azurelib/util/RenderUtils.java @@ -42,6 +42,14 @@ * Helper class for various methods and functions useful while rendering */ public final class RenderUtils { + private static final Matrix4f TRANSLATE_MATRIX_CACHE = new Matrix4f(); + + private static final Quaternionf X_QUATERNION_CACHE = new Quaternionf(); + + private static final Quaternionf Y_QUATERNION_CACHE = new Quaternionf(); + + private static final Quaternionf Z_QUATERNION_CACHE = new Quaternionf(); + public static void translateMatrixToBone(PoseStack poseStack, CoreGeoBone bone) { poseStack.translate(-bone.getPosX() / 16f, bone.getPosY() / 16f, bone.getPosZ() / 16f); } @@ -60,9 +68,9 @@ public static void rotateMatrixAroundBone(PoseStack poseStack, CoreGeoBone bone) public static void rotateMatrixAroundCube(PoseStack poseStack, GeoCube cube) { Vec3 rotation = cube.rotation(); - poseStack.mulPose(new Quaternionf().rotationXYZ(0, 0, (float) rotation.z())); - poseStack.mulPose(new Quaternionf().rotationXYZ(0, (float) rotation.y(), 0)); - poseStack.mulPose(new Quaternionf().rotationXYZ((float) rotation.x(), 0, 0)); + poseStack.mulPose(Z_QUATERNION_CACHE.rotationXYZ(0, 0, (float) rotation.z())); + poseStack.mulPose(Y_QUATERNION_CACHE.rotationXYZ(0, (float) rotation.y(), 0)); + poseStack.mulPose(X_QUATERNION_CACHE.rotationXYZ((float) rotation.x(), 0, 0)); } public static void scaleMatrixForBone(PoseStack poseStack, CoreGeoBone bone) { @@ -123,7 +131,8 @@ public static void faceRotation(PoseStack poseStack, Entity animatable, float pa * Add a positional vector to a matrix. This is specifically implemented to act as a translation of an x/y/z coordinate triplet to a render matrix */ public static Matrix4f translateMatrix(Matrix4f matrix, Vector3f vector) { - return matrix.add(new Matrix4f().m30(vector.x).m31(vector.y).m32(vector.z)); + TRANSLATE_MATRIX_CACHE.m30(vector.x).m31(vector.y).m32(vector.z); + return matrix.add(TRANSLATE_MATRIX_CACHE); } /** @@ -237,6 +246,7 @@ public static float getDirectionAngle(Direction direction) { * @return The GeoModel, or null if one isn't found */ @Nullable + @Deprecated(forRemoval = true) public static GeoModel getGeoModelForEntityType(EntityType entityType) { EntityRenderer renderer = Minecraft.getInstance().getEntityRenderDispatcher().renderers.get(entityType); @@ -250,6 +260,7 @@ public static GeoModel getGeoModelForEntityType(EntityType entityType) { * @return The {@code GeoAnimatable} instance, or null if one isn't found */ @Nullable + @Deprecated(forRemoval = true) public static GeoAnimatable getReplacedAnimatable(EntityType entityType) { EntityRenderer renderer = Minecraft.getInstance().getEntityRenderDispatcher().renderers.get(entityType); @@ -265,6 +276,7 @@ public static GeoAnimatable getReplacedAnimatable(EntityType entityType) { * @return The GeoModel, or null if one isn't found */ @Nullable + @Deprecated(forRemoval = true) public static GeoModel getGeoModelForEntity(Entity entity) { EntityRenderer renderer = Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(entity); @@ -280,6 +292,7 @@ public static GeoModel getGeoModelForEntity(Entity entity) { * @return The GeoModel, or null if one isn't found */ @Nullable + @Deprecated(forRemoval = true) public static GeoModel getGeoModelForItem(Item item) { if (RenderProvider.of(item).getCustomRenderer()instanceof GeoRenderer geoRenderer) return geoRenderer.getGeoModel(); @@ -296,6 +309,7 @@ public static GeoModel getGeoModelForItem(Item item) { * @return The GeoModel, or null if one isn't found */ @Nullable + @Deprecated(forRemoval = true) public static GeoModel getGeoModelForBlock(BlockEntity blockEntity) { BlockEntityRenderer renderer = Minecraft.getInstance().getBlockEntityRenderDispatcher().getRenderer(blockEntity); @@ -311,6 +325,7 @@ public static GeoModel getGeoModelForBlock(BlockEntity blockEntity) { * @return The GeoModel, or null if one isn't found */ @Nullable + @Deprecated(forRemoval = true) public static GeoModel getGeoModelForArmor(ItemStack stack) { if (RenderProvider.of(stack).getHumanoidArmorModel(null, stack, null, null)instanceof GeoArmorRenderer armorRenderer) return armorRenderer.getGeoModel(); diff --git a/fabric/build.gradle b/fabric/build.gradle index 3e566fe11..925e053cc 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -33,6 +33,7 @@ dependencies { modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}" compileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.1' modApi "com.terraformersmc:modmenu:${modmenu_version}" + include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:0.4.1"))) compileOnly project(":common") } diff --git a/fabric/src/main/resources/azurelib.fabric.mixins.json b/fabric/src/main/resources/azurelib.fabric.mixins.json index 99ce67cfd..427a6bc2c 100644 --- a/fabric/src/main/resources/azurelib.fabric.mixins.json +++ b/fabric/src/main/resources/azurelib.fabric.mixins.json @@ -10,6 +10,10 @@ ], "client": [ "AccessorWarningScreen", + "BlockEntityMixin_AzBlockEntityAnimatorCache", + "EntityMixin_AzEntityAnimatorCache", + "ItemStackMixin_AzItemAnimatorCache", + "ItemStackMixin_AzItemStackIdentityRegistry", "FabricMixinHumanoidArmorLayer", "ItemRendererAccessor", "MinecraftMixin", diff --git a/gradle.properties b/gradle.properties index 6364e5c1b..e9a176dbb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ mapping_version = 1.20.1 # Fabric fabric_version = 0.83.0+1.20.1 -fabric_loader_version = 0.14.21 +fabric_loader_version = 0.14.25 modmenu_version = 7.2.2 # Mod options diff --git a/neoforge/build.gradle b/neoforge/build.gradle index e4006484e..79afb023d 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -83,6 +83,10 @@ dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${neo_version}" compileOnly project(":common") annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT:processor") + compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")) + implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.4.1")) { + jarJar.ranged(it, "[0.4.1,)") + } } tasks.withType(JavaCompile).configureEach { diff --git a/neoforge/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java b/neoforge/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java new file mode 100644 index 000000000..311d9254b --- /dev/null +++ b/neoforge/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java @@ -0,0 +1,92 @@ +package mod.azure.azurelib.mixins; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.animatable.GeoItem; +import mod.azure.azurelib.animatable.client.RenderProvider; +import mod.azure.azurelib.renderer.GeoArmorRenderer; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +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; + +/** + * Render hook for injecting AzureLib's armor rendering functionalities + */ +@Mixin(value = HumanoidArmorLayer.class, priority = 700) +public class NeoMixinHumanoidArmorLayer> { + + @ModifyExpressionValue( + method = "renderArmorPiece", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;getItemBySlot(Lnet/minecraft/world/entity/EquipmentSlot;)Lnet/minecraft/world/item/ItemStack;" + ) + ) + private ItemStack azurelib$captureItemBySlot( + ItemStack original, + @Share("item_by_slot") LocalRef itemBySlotRef + ) { + itemBySlotRef.set(original); + return original; + } + + @Inject( + method = "renderArmorPiece", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;renderModel(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/Model;ZFFFLnet/minecraft/resources/ResourceLocation;)V" + ), cancellable = true + ) + public void azurelib$renderAzurelibModel( + PoseStack poseStack, + MultiBufferSource bufferSource, + T entity, + EquipmentSlot equipmentSlot, + int packedLight, + A baseModel, + CallbackInfo ci, + @Share("item_by_slot") LocalRef itemBySlotRef + ) { + var stack = itemBySlotRef.get(); + var renderProvider = RenderProvider.of(stack); + @SuppressWarnings("unchecked") + var humanoidModel = (HumanoidModel) baseModel; + var geckolibModel = renderProvider + .getGenericArmorModel(entity, stack, equipmentSlot, humanoidModel); + + if (geckolibModel != null && stack.getItem() instanceof GeoItem) { + if (geckolibModel instanceof GeoArmorRenderer geoArmorRenderer) { + geoArmorRenderer.prepForRender(entity, stack, equipmentSlot, baseModel); + } + + baseModel.copyPropertiesTo((A) geckolibModel); + + geckolibModel.renderToBuffer(poseStack, null, packedLight, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1); + ci.cancel(); + } + + var renderer = AzArmorRendererRegistry.getOrNull(stack.getItem()); + + if (renderer != null) { + var rendererPipeline = renderer.rendererPipeline(); + var armorModel = rendererPipeline.armorModel(); + @SuppressWarnings("unchecked") + var typedHumanoidModel = (HumanoidModel) armorModel; + + renderer.prepForRender(entity, stack, equipmentSlot, baseModel); + baseModel.copyPropertiesTo(typedHumanoidModel); + armorModel.renderToBuffer(poseStack, null, packedLight, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1); + ci.cancel(); + } + } +} \ No newline at end of file diff --git a/neoforge/src/main/resources/azurelib.forge.mixins.json b/neoforge/src/main/resources/azurelib.forge.mixins.json index 3fabba704..9dc524092 100644 --- a/neoforge/src/main/resources/azurelib.forge.mixins.json +++ b/neoforge/src/main/resources/azurelib.forge.mixins.json @@ -11,6 +11,10 @@ ], "client": [ "AccessorWarningScreen", + "BlockEntityMixin_AzBlockEntityAnimatorCache", + "EntityMixin_AzEntityAnimatorCache", + "ItemStackMixin_AzItemAnimatorCache", + "ItemStackMixin_AzItemStackIdentityRegistry", "ClientHooksMixin", "ItemRendererAccessor", "MinecraftMixin", From 8f8724c930f548c495b8bd887b76c333b6c6ef9a Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:50:09 -0400 Subject: [PATCH 02/40] More back port work, 19 classes left. --- .../azure/azurelib/cache/AzureLibCache.java | 3 +- .../azurelib/core/molang/MolangQueries.java | 56 ++++++--- ...tackMixin_AzItemStackIdentityRegistry.java | 17 +-- .../azure/azurelib/mixins/MinecraftMixin.java | 2 +- .../azurelib/mixins/MixinItemRenderer.java | 21 +++- .../azurelib/mixins/PlayerListMixin.java | 2 +- .../azurelib/mixins/TextureManagerMixin.java | 2 +- .../AzBlockEntityDispatchCommandPacket.java | 53 ++++----- .../packet/AzEntityDispatchCommandPacket.java | 35 ++++-- .../AzItemStackDispatchCommandPacket.java | 36 ++++-- .../rewrite/animation/AzAnimator.java | 6 +- .../animation/dispatch/AzDispatchSide.java | 16 +-- .../animation/dispatch/command/AzCommand.java | 31 ++--- .../command/AzRootCommandBuilder.java | 2 +- .../dispatch/command/action/AzAction.java | 10 +- .../command/action/codec/AzActionCodec.java | 34 ++---- .../action/impl/root/AzRootCancelAction.java | 65 +++++++---- .../impl/root/AzRootCancelAllAction.java | 33 ++---- .../AzRootPlayAnimationSequenceAction.java | 29 ++--- .../root/AzRootSetAnimationSpeedAction.java | 22 +--- .../impl/root/AzRootSetEasingTypeAction.java | 25 +--- .../root/AzRootSetTransitionSpeedAction.java | 34 +++--- .../action/registry/AzActionRegistry.java | 102 ++++++----------- .../command/sequence/AzAnimationSequence.java | 18 +-- .../command/stage/AzAnimationStage.java | 23 ++-- .../animation/easing/AzEasingType.java | 15 +-- .../codec/AzAnimationPropertiesCodec.java | 4 +- .../AzAnimationStagePropertiesCodec.java | 4 +- .../rewrite/render/AzRendererConfig.java | 46 ++++++-- .../render/AzRendererPipelineContext.java | 13 +-- .../render/armor/AzArmorRendererConfig.java | 22 ++++ .../render/armor/AzArmorRendererPipeline.java | 5 +- .../block/AzBlockEntityModelRenderer.java | 2 +- .../block/AzBlockEntityRendererConfig.java | 22 ++++ .../block/AzBlockEntityRendererPipeline.java | 5 +- .../render/entity/AzEntityRendererConfig.java | 31 +++++ .../entity/AzEntityRendererPipeline.java | 5 +- .../rewrite/render/item/AzItemRenderer.java | 2 +- .../render/item/AzItemRendererConfig.java | 33 +++++- .../render/item/AzItemRendererPipeline.java | 5 +- .../rewrite/render/layer/AzArmorLayer.java | 76 ++++++------ .../render/layer/AzBlockAndItemLayer.java | 108 ++++++++++-------- .../rewrite/util/codec/AzListStreamCodec.java | 34 ++++++ .../mod/azure/azurelib/util/RenderUtils.java | 5 +- .../resources/assets/azurelib/lang/es_es.json | 54 +++++++++ .../resources/assets/azurelib/lang/fr_fr.json | 23 +++- .../resources/assets/azurelib/lang/ru_ru.json | 54 +++++++++ .../resources/assets/azurelib/lang/zh_cn.json | 55 +++++++++ .../platform/FabricAzureLibNetwork.java | 18 +-- .../azurelib/mixins/ClientHooksMixin.java | 14 ++- .../platform/NeoForgeAzureLibNetwork.java | 4 + 51 files changed, 842 insertions(+), 494 deletions(-) create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/util/codec/AzListStreamCodec.java create mode 100644 common/src/main/resources/assets/azurelib/lang/es_es.json create mode 100644 common/src/main/resources/assets/azurelib/lang/ru_ru.json create mode 100644 common/src/main/resources/assets/azurelib/lang/zh_cn.json diff --git a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java index 6d45e7adc..d7d55b7bc 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java +++ b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java @@ -7,7 +7,6 @@ import mod.azure.azurelib.cache.object.BakedGeoModel; import mod.azure.azurelib.core.animatable.model.CoreGeoModel; import mod.azure.azurelib.loading.FileLoader; -import mod.azure.azurelib.loading.json.FormatVersion; import mod.azure.azurelib.loading.json.raw.Model; import mod.azure.azurelib.loading.object.BakedAnimations; import mod.azure.azurelib.loading.object.BakedModelFactory; @@ -57,7 +56,7 @@ public static Map getBakedModels() { } public static void registerReloadListener() { - Minecraft mc = Minecraft.getInstance(); + var mc = Minecraft.getInstance(); if (mc == null) return; diff --git a/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java b/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java index a0cd8e7f1..4552ec98c 100644 --- a/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java +++ b/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java @@ -12,20 +12,48 @@ * These do not constitute a definitive list of queries; merely the default ones */ public final class MolangQueries { - public static final String ANIM_TIME = "query.anim_time"; - public static final String LIFE_TIME = "query.life_time"; - public static final String ACTOR_COUNT = "query.actor_count"; - public static final String TIME_OF_DAY = "query.time_of_day"; - public static final String MOON_PHASE = "query.moon_phase"; - public static final String DISTANCE_FROM_CAMERA = "query.distance_from_camera"; - public static final String IS_ON_GROUND = "query.is_on_ground"; - public static final String IS_IN_WATER = "query.is_in_water"; - public static final String IS_IN_WATER_OR_RAIN = "query.is_in_water_or_rain"; - public static final String HEALTH = "query.health"; - public static final String MAX_HEALTH = "query.max_health"; - public static final String IS_ON_FIRE = "query.is_on_fire"; - public static final String GROUND_SPEED = "query.ground_speed"; - public static final String YAW_SPEED = "query.yaw_speed"; + + private static final String QUERY_PREFIX = "query."; + + private static final String SHORT_PREFIX = "q."; + + public static final String ANIM_TIME = normalize("query.anim_time"); + + public static final String LIFE_TIME = normalize("query.life_time"); + + public static final String ACTOR_COUNT = normalize("query.actor_count"); + + public static final String TIME_OF_DAY = normalize("query.time_of_day"); + + public static final String MOON_PHASE = normalize("query.moon_phase"); + + public static final String DISTANCE_FROM_CAMERA = normalize("query.distance_from_camera"); + + public static final String IS_ON_GROUND = normalize("query.is_on_ground"); + + public static final String IS_IN_WATER = normalize("query.is_in_water"); + + public static final String IS_IN_WATER_OR_RAIN = normalize("query.is_in_water_or_rain"); + + public static final String HEALTH = normalize("query.health"); + + public static final String MAX_HEALTH = normalize("query.max_health"); + + public static final String IS_ON_FIRE = normalize("query.is_on_fire"); + + public static final String GROUND_SPEED = normalize("query.ground_speed"); + + public static final String YAW_SPEED = normalize("query.yaw_speed"); + + private static String normalize(String queryName) { + if (queryName.startsWith(QUERY_PREFIX)) { + return queryName; + } else if (queryName.startsWith(SHORT_PREFIX)) { + return QUERY_PREFIX + queryName.substring(SHORT_PREFIX.length()); + } else { + throw new IllegalArgumentException("Invalid query name: " + queryName); + } + } private MolangQueries() { throw new UnsupportedOperationException(); diff --git a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java index 1295f25e8..e8898960a 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java @@ -12,14 +12,15 @@ import java.util.UUID; /** - * A mixin class for modifying the initialization behavior of the {@link ItemStack} class. This mixin specifically - * ensures that a unique identity component is added to an {@link ItemStack} upon creation, provided that the associated - * item has been registered in the {@link AzIdentityRegistry}. When an {@link ItemStack} is instantiated, the mixin - * checks if: - The item has an identity registered in {@link AzIdentityRegistry}. - The provided - * {@link PatchedDataComponentMap} does not already contain an `az_id` component. If both conditions are met, the mixin - * assigns a universally unique identifier (UUID) as the `az_id` component to the associated - * {@link PatchedDataComponentMap}. This mechanism enables unique identification and tracking of specific item stacks in - * the game. + * A mixin class for injecting additional functionality into the {@link ItemStack} + * constructor to handle identity registration via AzureLib. + * + * This mixin ensures that every {@link ItemStack} is assigned a unique identifier + * when its corresponding item has been registered in the {@link AzIdentityRegistry} and + * no existing UUID is present in the item's {@link CompoundTag}. + * + * The mixin method `az_addIdentityComponent` is invoked at the "TAIL" of the + * {@link ItemStack} constructor, which takes a {@link CompoundTag} as a parameter. */ @Mixin(ItemStack.class) public class ItemStackMixin_AzItemStackIdentityRegistry { diff --git a/common/src/main/java/mod/azure/azurelib/mixins/MinecraftMixin.java b/common/src/main/java/mod/azure/azurelib/mixins/MinecraftMixin.java index 47edeb44b..87771d453 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/MinecraftMixin.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/MinecraftMixin.java @@ -35,7 +35,7 @@ public MinecraftMixin(String p_i50401_1_) { } @Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;resetData()V")) - private void configuration_reloadClientConfigs(Screen screen, CallbackInfo ci) { + private void azurelib$reloadClientConfigs(Screen screen, CallbackInfo ci) { ConfigHolder.getSynchronizedConfigs().stream() .map(ConfigHolder::getConfig) .filter(Optional::isPresent) diff --git a/common/src/main/java/mod/azure/azurelib/mixins/MixinItemRenderer.java b/common/src/main/java/mod/azure/azurelib/mixins/MixinItemRenderer.java index 789ef5851..ac6c4fa69 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/MixinItemRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/MixinItemRenderer.java @@ -1,5 +1,6 @@ package mod.azure.azurelib.mixins; +import mod.azure.azurelib.rewrite.render.item.AzItemRendererRegistry; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -21,8 +22,22 @@ @Mixin(ItemRenderer.class) public class MixinItemRenderer { @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/BlockEntityWithoutLevelRenderer;renderByItem(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;II)V"), cancellable = true) - public void itemModelHook(ItemStack itemStack, ItemDisplayContext transformType, boolean bl, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, int j, BakedModel bakedModel, CallbackInfo ci) { - if (itemStack.getItem() instanceof GeoItem) - RenderProvider.of(itemStack).getCustomRenderer().renderByItem(itemStack, transformType, poseStack, multiBufferSource, i, j); + public void azurelib$itemModelHook(ItemStack itemStack, ItemDisplayContext transformType, boolean bl, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, int j, BakedModel bakedModel, CallbackInfo ci) { + // TODO: Remove this along with Geo-code. + if (itemStack.getItem() instanceof GeoItem) { + RenderProvider.of(itemStack) + .getCustomRenderer() + .renderByItem(itemStack, transformType, poseStack, multiBufferSource, i, j); + } + + var item = itemStack.getItem(); + var renderer = AzItemRendererRegistry.getOrNull(item); + + if (renderer != null) { + switch (transformType) { + case GUI -> renderer.renderByGui(itemStack, poseStack, multiBufferSource, i); + default -> renderer.renderByItem(itemStack, poseStack, multiBufferSource, i); + } + } } } diff --git a/common/src/main/java/mod/azure/azurelib/mixins/PlayerListMixin.java b/common/src/main/java/mod/azure/azurelib/mixins/PlayerListMixin.java index a29b579c9..f767c2f99 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/PlayerListMixin.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/PlayerListMixin.java @@ -28,7 +28,7 @@ public abstract class PlayerListMixin { @Inject(method = "placeNewPlayer", at = @At("TAIL")) - private void configuration_sendServerConfigs(Connection connection, ServerPlayer player, CallbackInfo ci) { + private void azurelib$sendServerConfigs(Connection connection, ServerPlayer player, CallbackInfo ci) { Set set = ConfigHolder.getSynchronizedConfigs(); set.forEach(id -> Services.NETWORK.sendClientPacket(player, id)); } diff --git a/common/src/main/java/mod/azure/azurelib/mixins/TextureManagerMixin.java b/common/src/main/java/mod/azure/azurelib/mixins/TextureManagerMixin.java index 6904f5640..0242e1a0a 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/TextureManagerMixin.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/TextureManagerMixin.java @@ -28,7 +28,7 @@ public abstract class TextureManagerMixin { @Shadow public abstract void register(ResourceLocation resourceLocation, AbstractTexture abstractTexture); @Inject(method = "getTexture(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/texture/AbstractTexture;", at = @At("HEAD")) - private void wrapAnimatableTexture(ResourceLocation path, CallbackInfoReturnable callback) { + private void azurelib$wrapAnimatableTexture(ResourceLocation path, CallbackInfoReturnable callback) { AbstractTexture existing = this.byPath.get(path); if (existing == null) { diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java index a06daf2fb..bc487ee73 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java @@ -12,46 +12,46 @@ public class AzBlockEntityDispatchCommandPacket extends AbstractPacket { - private final BlockPos blockPos; - private final AzCommand dispatchCommand; - + // TODO: Updated encode/receive methods for AzCommand.CODEC/dispatchCommand public static final StreamCodec CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, - AzBlockEntityDispatchCommandPacket::blockPos, - AzCommand.CODEC, - AzBlockEntityDispatchCommandPacket::dispatchCommand, - AzBlockEntityDispatchCommandPacket::new + BlockPos.STREAM_CODEC, + AzBlockEntityDispatchCommandPacket::blockPos, + AzCommand.CODEC, + AzBlockEntityDispatchCommandPacket::dispatchCommand, + AzBlockEntityDispatchCommandPacket::new ); - public AzBlockEntityDispatchCommandPacket(BlockPos blockPos, AzCommand dispatchCommand) { + private final BlockPos blockPos; + private final AzCommand dispatchCommand; + + public AzBlockEntityDispatchCommandPacket( + BlockPos blockPos, + AzCommand dispatchCommand + ) { this.blockPos = blockPos; this.dispatchCommand = dispatchCommand; } - public BlockPos blockPos() { - return blockPos; - } - - public AzCommand dispatchCommand() { - return dispatchCommand; - } - @Override public void encode(FriendlyByteBuf buf) { buf.writeBlockPos(this.blockPos); - AzCommand.encode(buf, dispatchCommand()); + // TODO: Needs fixed + // AzCommand.CODEC } - public static AzBlockEntityDispatchCommandPacket decode(FriendlyByteBuf buf) { - var blockPos = buf.readBlockPos(); + @Override + public ResourceLocation getPacketID() { + return AzureLibNetwork.AZ_BLOCKENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID; + } - // Deserialize the AzCommand - AzCommand command = AzCommand.decode(buf); + public static AzBlockEntityDispatchCommandPacket receive(FriendlyByteBuf buf) { + var pos = buf.readBlockPos(); + // TODO: Needs fixed + // AzCommand azCommand = buf.readUtf(); - return new AzBlockEntityDispatchCommandPacket(blockPos, dispatchCommand()); + return new AzBlockEntityDispatchCommandPacket(pos, azCommand); } - @Override public void handle() { var blockEntity = ClientUtils.getLevel().getBlockEntity(blockPos); @@ -66,9 +66,4 @@ public void handle() { dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator)); } } - - @Override - public ResourceLocation getPacketID() { - return AzureLibNetwork.AZ_BLOCKENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID; - } } diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java index 1b0a54939..513f85771 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java @@ -11,10 +11,22 @@ public class AzEntityDispatchCommandPacket extends AbstractPacket { + // TODO: Updated encode/receive methods for AzCommand.CODEC/dispatchCommand + public static final StreamCodec CODEC = StreamCodec.composite( + ByteBufCodecs.VAR_INT, + AzEntityDispatchCommandPacket::entityId, + AzCommand.CODEC, + AzEntityDispatchCommandPacket::dispatchCommand, + AzEntityDispatchCommandPacket::new + ); + private final int entityId; private final AzCommand dispatchCommand; - public AzEntityDispatchCommandPacket(int entityId, AzCommand dispatchCommand) { + public AzEntityDispatchCommandPacket( + int entityId, + AzCommand dispatchCommand + ) { this.entityId = entityId; this.dispatchCommand = dispatchCommand; } @@ -22,11 +34,23 @@ public AzEntityDispatchCommandPacket(int entityId, AzCommand dispatchCommand) { @Override public void encode(FriendlyByteBuf buf) { buf.writeInt(this.entityId); - // TODO: Fix this, as I don't believe this is correct. - //buf.writeByte(dispatchCommand.actions().size()); + // TODO: Needs fixed + // AzCommand.CODEC } @Override + public ResourceLocation getPacketID() { + return AzureLibNetwork.AZ_ENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID; + } + + public static AzItemStackDispatchCommandPacket receive(FriendlyByteBuf buf) { + var entityId = buf.readInt(); + // TODO: Needs fixed + // AzCommand azCommand = buf.readUtf(); + + return new AzItemStackDispatchCommandPacket(entityId, azCommand); + } + public void handle() { var entity = ClientUtils.getLevel().getEntity(this.entityId); @@ -40,9 +64,4 @@ public void handle() { dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator)); } } - - @Override - public ResourceLocation getPacketID() { - return AzureLibNetwork.AZ_ENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID; - } } diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java index 06694054a..7f239d3e4 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java @@ -5,6 +5,7 @@ import mod.azure.azurelib.rewrite.animation.cache.AzIdentifiableItemStackAnimatorCache; import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; +import net.minecraft.core.UUIDUtil; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; @@ -12,10 +13,22 @@ public class AzItemStackDispatchCommandPacket extends AbstractPacket { + // TODO: Updated encode/receive methods for AzCommand.CODEC/dispatchCommand + public static final StreamCodec CODEC = StreamCodec.composite( + UUIDUtil.STREAM_CODEC, + AzItemStackDispatchCommandPacket::itemStackId, + AzCommand.CODEC, + AzItemStackDispatchCommandPacket::dispatchCommand, + AzItemStackDispatchCommandPacket::new + ); + private final UUID itemStackId; private final AzCommand dispatchCommand; - public AzItemStackDispatchCommandPacket(UUID itemStackId, AzCommand dispatchCommand) { + public AzItemStackDispatchCommandPacket( + UUID itemStackId, + AzCommand dispatchCommand + ) { this.itemStackId = itemStackId; this.dispatchCommand = dispatchCommand; } @@ -23,11 +36,23 @@ public AzItemStackDispatchCommandPacket(UUID itemStackId, AzCommand dispatchComm @Override public void encode(FriendlyByteBuf buf) { buf.writeUUID(this.itemStackId); - // TODO: Fix this, as I don't believe this is correct. - //buf.writeByte(dispatchCommand.actions().size()); + // TODO: Needs fixed + // AzCommand.CODEC } @Override + public ResourceLocation getPacketID() { + return AzureLibNetwork.AZ_ITEM_STACK_DISPATCH_COMMAND_SYNC_PACKET_ID; + } + + public static AzItemStackDispatchCommandPacket receive(FriendlyByteBuf buf) { + var readUUID = buf.readUUID(); + // TODO: Needs fixed + // AzCommand azCommand = buf.readUtf(); + + return new AzItemStackDispatchCommandPacket(readUUID, azCommand); + } + public void handle() { var animator = AzIdentifiableItemStackAnimatorCache.getInstance().getOrNull(itemStackId); @@ -35,9 +60,4 @@ public void handle() { dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator)); } } - - @Override - public ResourceLocation getPacketID() { - return AzureLibNetwork.AZ_ITEM_STACK_DISPATCH_COMMAND_SYNC_PACKET_ID; - } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java index bc707291d..8f290d2d1 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java @@ -79,9 +79,13 @@ protected void preAnimationSetup(T animatable, double animTime) { } protected void applyMolangQueries(T animatable, double animTime) { - var level = Objects.requireNonNull(Minecraft.getInstance().level); + var level = Minecraft.getInstance().level; var parser = MolangParser.INSTANCE; + if (level == null) { + return; + } + parser.setMemoizedValue(MolangQueries.LIFE_TIME, () -> animTime / 20d); parser.setMemoizedValue(MolangQueries.ACTOR_COUNT, level::getEntityCount); parser.setMemoizedValue(MolangQueries.TIME_OF_DAY, () -> level.getDayTime() / 24000f); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java index a5e97470b..d2da7d054 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java @@ -34,18 +34,10 @@ public enum AzDispatchSide implements StringRepresentable { this.id = id; } - public void encode(@NotNull FriendlyByteBuf buf, @NotNull AzDispatchSide val) { - buf.writeByte(val.id); - } - - public AzDispatchSide decode(@NotNull FriendlyByteBuf buf) { - int id = buf.readByte(); - AzDispatchSide side = ID_TO_ENUM_MAP.get(id); - if (side == null) { - throw new IllegalArgumentException("Invalid AzDispatchSide ID: " + id); - } - return side; - } + public static final StreamCodec CODEC = StreamCodec.of( + (buf, val) -> buf.writeByte(val.id), + buf -> ID_TO_ENUM_MAP.get((int) buf.readByte()) + ); @Override public @NotNull String getSerializedName() { diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java index fd797d6bc..3b59f78ee 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java @@ -10,6 +10,7 @@ import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehavior; import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviors; +import mod.azure.azurelib.rewrite.util.codec.AzListStreamCodec; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; @@ -30,29 +31,11 @@ */ public record AzCommand(List actions) { - public void encode(FriendlyByteBuf buf, AzCommand command) { - // Write the size of the actions list - buf.writeInt(actions.size()); - - // Encode each AzAction in the list - for (AzAction action : actions) { - actions.encode(buf, action); - } - } - - public static AzCommand decode(FriendlyByteBuf buf) { - // Read the size of the actions list - int size = buf.readInt(); - - // Decode each AzAction and collect them into a list - for (int i = 0; i < size; i++) { - actions.add(AzAction.decode(buf)); - } - - // Return the new AzCommand with the decoded actions - return new AzCommand(actions); - } - + public static final StreamCodec CODEC = StreamCodec.composite( + new AzListStreamCodec<>(AzAction.CODEC), + AzCommand::actions, + AzCommand::new + ); public static AzRootCommandBuilder builder() { return new AzRootCommandBuilder(); @@ -149,7 +132,7 @@ public void sendForItem(Entity entity, ItemStack itemStack) { if (entity.level().isClientSide()) { dispatchFromClient(entity); } else { - var uuid = itemStack.getTag().getUUID("az_id"); + var uuid = itemStack.get(AzureLib.AZ_ID.get()); if (uuid == null) { AzureLib.LOGGER.warn( diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzRootCommandBuilder.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzRootCommandBuilder.java index c19be47a2..5d7fc9bd6 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzRootCommandBuilder.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzRootCommandBuilder.java @@ -14,7 +14,7 @@ public AzRootCommandBuilder append(AzCommand command) { } public AzRootCommandBuilder cancelAll() { - actions.add(new AzRootCancelAllAction()); + actions.add(AzRootCancelAllAction.INSTANCE); return this; } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java index 0de6ad02e..c0de1bf4a 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java @@ -2,10 +2,8 @@ import mod.azure.azurelib.rewrite.animation.AzAnimator; import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; -import mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootCancelAction; -import net.minecraft.network.FriendlyByteBuf; +import mod.azure.azurelib.rewrite.animation.dispatch.command.action.codec.AzActionCodec; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; /** * The AzAction interface serves as a base contract for defining actions that can be dispatched within the animation @@ -15,11 +13,9 @@ */ public interface AzAction { + AzActionCodec CODEC = new AzActionCodec(); + void handle(AzDispatchSide originSide, AzAnimator animator); ResourceLocation getResourceLocation(); - - void encode(@NotNull FriendlyByteBuf buf, @NotNull T action); - - T decode(@NotNull FriendlyByteBuf buf); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java index deb77b4be..8b7f3726d 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java @@ -3,7 +3,6 @@ import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; import mod.azure.azurelib.rewrite.animation.dispatch.command.action.registry.AzActionRegistry; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; import org.jetbrains.annotations.NotNull; /** @@ -16,41 +15,34 @@ * Use this implementation in scenarios where AzAction objects need to be serialized or deserialized for efficient data * transmission or storage. */ -public class AzActionCodec { +public class AzActionCodec implements StreamCodec { - public static AzAction decode(FriendlyByteBuf byteBuf) { - // Read the ID of the action + @Override + public @NotNull AzAction decode(@NotNull FriendlyByteBuf byteBuf) { var id = byteBuf.readShort(); - - // Retrieve the corresponding codec from the registry var codec = AzActionRegistry - .getActionClassOrNull(id); + .>getCodecOrNull(id); - // Throw an error if the codec is not found if (codec == null) { throw new NullPointerException( - "Could not find action codec for a given action id while decoding data. ID: " + id + "Could not find action codec for a given action id while decoding data. ID: " + id ); } - // Use the codec to decode the action return codec.decode(byteBuf); } - public static void encode(FriendlyByteBuf byteBuf, AzAction action) { - // Get the resource location of the action + @Override + public void encode(@NotNull FriendlyByteBuf byteBuf, @NotNull AzAction action) { var resourceLocation = action.getResourceLocation(); - - // Retrieve the ID and the corresponding codec for the action var id = AzActionRegistry.getIdOrNull(resourceLocation); var codec = AzActionRegistry - .getCodecOrNull(resourceLocation); + .>getCodecOrNull(resourceLocation); - // Throw an error if either the ID or the codec is not found if (id == null) { throw new NullPointerException( - "Could not find action id for a given resource location while encoding data. Resource Location: " - + resourceLocation + "Could not find action id for a given resource location while encoding data. Resource Location: " + + resourceLocation ); } @@ -58,13 +50,11 @@ public static void encode(FriendlyByteBuf byteBuf, AzAction action) { if (codec == null) { throw new NullPointerException( - "Could not find action codec for a given resource location while encoding data. Resource Location: " - + resourceLocation + ", ID: " + id + "Could not find action codec for a given resource location while encoding data. Resource Location: " + + resourceLocation + ", ID: " + id ); } - // Use the codec to encode the action codec.encode(byteBuf, action); } - } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java index 70902413f..1f00b588d 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java @@ -6,22 +6,53 @@ import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; /** - * Represents an action to cancel the current animation, associated with a specific animation controller by its name. - * This action is part of the AzureLib animation framework and is used to stop an ongoing animation - * by clearing the current animation state of the specified controller. - *
- * This class implements the {@link AzAction} interface, allowing it to be dispatched - * to modify animation states within an {@link AzAnimator}. - *
- * The action can be serialized and deserialized for network communication or storage purposes. + * Represents an action that cancels the current animation of a specified animation controller in the animation system. + * This action is part of the root-level dispatch actions and interacts with a specific animation controller by name. + *
+ *
+ * An instance of this record is serialized and deserialized using the {@code CODEC}, and it is associated with a unique + * resource location defined by {@code RESOURCE_LOCATION}.
+ *
+ * When executed, the {@code handle} method ensures that the animation of the targeted controller is stopped by setting + * its current animation to {@code null}.
+ *
+ * This class is primarily used within the {@code AzAnimator} context where each animation controller is part of the + * animator's controller container.
+ *
+ * Implements: - {@link AzAction}: Allows the action to be dispatched within the animation system.
+ *
+ * Fields: + *
    + *
  • {@code controllerName}: The name of the animation controller which this action targets.
  • + *
+ *
+ *
+ * Constants: + *
    + *
  • {@code CODEC}: A codec for encoding and decoding this action during network communication.
  • + *
  • {@code RESOURCE_LOCATION}: A unique identifier for this action.
  • + *
+ *
+ *
+ * Methods: + *
    + *
  • {@code handle(AzAnimator animator)}: Stops the current animation of the specified controller within the + * animator's animation controller container.
  • + *
  • {@code getResourceLocation()}: Returns the unique resource location associated with this action.
  • + *
*/ public record AzRootCancelAction( String controllerName ) implements AzAction { + public static final StreamCodec CODEC = StreamCodec.composite( + ByteBufCodecs.STRING_UTF8, + AzRootCancelAction::controllerName, + AzRootCancelAction::new + ); + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/cancel"); @Override @@ -37,20 +68,4 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } - - @Override - public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { - var cancelAction = (AzRootCancelAction) action; - buf.writeUtf(cancelAction.controllerName); - } - - @Override - public T decode(@NotNull FriendlyByteBuf buf) { - String controllerName = buf.readUtf(); - try { - return actionClass.getDeclaredConstructor(String.class).newInstance(controllerName); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException("Failed to decode action for class: " + actionClass.getName(), e); - } - } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java index 31af91818..3c0b55062 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java @@ -6,18 +6,23 @@ import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; - +/** + * The AzRootCancelAllAction class implements the AzAction interface and defines an action that cancels all ongoing + * animations within an animator by setting the current animation of all controllers to null.
+ * This class is designed to work within a system that manages animations for objects using animation controllers. Once + * this action is handled, all animation controllers associated with a specific animator will have their current + * animations cleared. + */ public class AzRootCancelAllAction implements AzAction { public static final AzRootCancelAllAction INSTANCE = new AzRootCancelAllAction(); + public static final StreamCodec CODEC = StreamCodec.unit(INSTANCE); + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/cancel_all"); - public static AzRootCancelAllAction getInstance() { - return INSTANCE; - } + private AzRootCancelAllAction() {} @Override public void handle(AzDispatchSide originSide, AzAnimator animator) { @@ -31,22 +36,4 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } - - @Override - public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { - - } - - @Override - public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { - if (!AzRootCancelAllAction.class.equals(actionClass)) { - throw new IllegalArgumentException("Unsupported action class: " + actionClass.getName()); - } - - // Safe cast because we ensured the class type is AzRootCancelAllAction - @SuppressWarnings("unchecked") - T action = (T) INSTANCE; - - return action; - } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java index c876021bd..77b828ec5 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java @@ -7,13 +7,20 @@ import mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequence; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; public record AzRootPlayAnimationSequenceAction( String controllerName, AzAnimationSequence sequence ) implements AzAction { + public static final StreamCodec CODEC = StreamCodec.composite( + ByteBufCodecs.STRING_UTF8, + AzRootPlayAnimationSequenceAction::controllerName, + AzAnimationSequence.CODEC, + AzRootPlayAnimationSequenceAction::sequence, + AzRootPlayAnimationSequenceAction::new + ); + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/play_animation_sequence"); @Override @@ -29,24 +36,4 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } - - @Override - public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { - var rootAction = (AzRootPlayAnimationSequenceAction) action; - buf.writeUtf(rootAction.controllerName()); - AzAnimationSequence.encode(buf, rootAction.sequence()); - } - - @Override - public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { - if (!AzRootPlayAnimationSequenceAction.class.equals(actionClass)) { - throw new IllegalArgumentException("Unsupported action class: " + actionClass.getName()); - } - - var controllerName = buf.readUtf(); - - @SuppressWarnings("unchecked") - T action = (T) new AzRootPlayAnimationSequenceAction(controllerName, sequence); - return action; - } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java index 445a9ff05..826e92fd5 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java @@ -6,12 +6,17 @@ import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; public record AzRootSetAnimationSpeedAction( double animationSpeed ) implements AzAction { + public static final StreamCodec CODEC = StreamCodec.composite( + ByteBufCodecs.DOUBLE, + AzRootSetAnimationSpeedAction::animationSpeed, + AzRootSetAnimationSpeedAction::new + ); + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/set_animation_speed"); @Override @@ -25,21 +30,6 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { ); } - @Override - public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { - buf.writeDouble(animationSpeed); - } - - @Override - public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { - Double animationSpeed = buf.readDouble(); - try { - return actionClass.getDeclaredConstructor(Double.class).newInstance(animationSpeed); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException("Failed to decode action for class: " + actionClass.getName(), e); - } - } - @Override public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java index 2b82c1c3b..b6c61b8bd 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java @@ -7,12 +7,17 @@ import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; public record AzRootSetEasingTypeAction( AzEasingType easingType ) implements AzAction { + public static final StreamCodec CODEC = StreamCodec.composite( + AzEasingType.STREAM_CODEC, + AzRootSetEasingTypeAction::easingType, + AzRootSetEasingTypeAction::new + ); + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/set_easing_type"); @Override @@ -30,22 +35,4 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } - - @Override - public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { - var easingAction = (AzRootSetEasingTypeAction) action; - AzEasingType.encode(buf, easingAction.easingType()); - } - - @Override - public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { - if (!AzRootSetEasingTypeAction.class.equals(actionClass)) { - throw new IllegalArgumentException("Unsupported action class: " + actionClass.getName()); - } - - @SuppressWarnings("unchecked") - T action = (T) new AzRootSetEasingTypeAction(easingType); - return action; - - } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java index 073a99618..0d912523d 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java @@ -6,12 +6,24 @@ import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; +/** + * The {@code AzRootSetTransitionSpeedAction} class implements the {@link AzAction} interface and represents an action + * that modifies the transition speed for an animator during an animation state. This action is intended for use within + * the animation system to adjust the transition timing of animations. This class provides a unique resource location + * identifier for this specific action and handles the logic required to apply the transition speed modification to the + * target {@link AzAnimator}. It utilizes {@link StreamCodec} for serialization and deserialization of this action. + */ public record AzRootSetTransitionSpeedAction( float transitionSpeed ) implements AzAction { + public static final StreamCodec CODEC = StreamCodec.composite( + ByteBufCodecs.FLOAT, + AzRootSetTransitionSpeedAction::transitionSpeed, + AzRootSetTransitionSpeedAction::new + ); + public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/set_transition_speed"); @Override @@ -29,24 +41,4 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } - - @Override - public void encode(@NotNull FriendlyByteBuf buf, @NotNull T action) { - var speedAction = (AzRootSetTransitionSpeedAction) action; - buf.writeFloat(speedAction.transitionSpeed()); - } - - @Override - public T decode(@NotNull FriendlyByteBuf buf, @NotNull Class actionClass) { - if (!AzRootSetTransitionSpeedAction.class.equals(actionClass)) { - throw new IllegalArgumentException("Unsupported action class: " + actionClass.getName()); - } - - var transitionSpeed = buf.readFloat(); - - @SuppressWarnings("unchecked") - T action = (T) new AzRootSetTransitionSpeedAction(transitionSpeed); - return action; - - } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java index 030e9843a..13de68e4a 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java @@ -1,10 +1,10 @@ package mod.azure.azurelib.rewrite.animation.dispatch.command.action.registry; import it.unimi.dsi.fastutil.objects.Object2ShortArrayMap; -import mod.azure.azurelib.AzureLibException; import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; import mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.*; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.Nullable; @@ -12,28 +12,34 @@ import java.util.Map; /** - * The AzActionRegistry class is responsible for managing the registration and resolution of actions - * (implementations of {@link AzAction}) within an animation system. It serves as a centralized registry - * where each action is associated with a unique {@link ResourceLocation} and an internally generated - * identifier for efficient lookup. + * The AzActionRegistry class serves as a centralized registry for mapping {@link AzAction} implementations to their + * associated {@link ResourceLocation} identifiers and codecs. This registry enables efficient encoding, decoding, and + * dispatching of animation-related actions within the animation system.
+ * Key Responsibilities: + *
    + *
  • Maintain a bidirectional mapping between {@link ResourceLocation} identifiers and short integer IDs for efficient + * serialization/deserialization.
  • + *
  • Register {@link AzAction} implementations and their corresponding {@link StreamCodec} instances.
  • + *
  • Provide methods for retrieving codecs and IDs based on resource locations or integer IDs. + *
*/ public class AzActionRegistry { private static final Map RESOURCE_LOCATION_TO_ID = new Object2ShortArrayMap<>(); - private static final Map> ACTION_CLASS_BY_ID = + private static final Map> CODEC_BY_ID = new HashMap<>(); private static short NEXT_FREE_ID = 0; static { // Root actions - register(AzRootCancelAction.RESOURCE_LOCATION, AzRootCancelAction.class); - register(AzRootCancelAllAction.RESOURCE_LOCATION, AzRootCancelAllAction.class); - register(AzRootPlayAnimationSequenceAction.RESOURCE_LOCATION, AzRootPlayAnimationSequenceAction.class); - register(AzRootSetAnimationSpeedAction.RESOURCE_LOCATION, AzRootSetAnimationSpeedAction.class); - register(AzRootSetEasingTypeAction.RESOURCE_LOCATION, AzRootSetEasingTypeAction.class); - register(AzRootSetTransitionSpeedAction.RESOURCE_LOCATION, AzRootSetTransitionSpeedAction.class); + register(AzRootCancelAction.RESOURCE_LOCATION, AzRootCancelAction.CODEC); + register(AzRootCancelAllAction.RESOURCE_LOCATION, AzRootCancelAllAction.CODEC); + register(AzRootPlayAnimationSequenceAction.RESOURCE_LOCATION, AzRootPlayAnimationSequenceAction.CODEC); + register(AzRootSetAnimationSpeedAction.RESOURCE_LOCATION, AzRootSetAnimationSpeedAction.CODEC); + register(AzRootSetEasingTypeAction.RESOURCE_LOCATION, AzRootSetEasingTypeAction.CODEC); + register(AzRootSetTransitionSpeedAction.RESOURCE_LOCATION, AzRootSetTransitionSpeedAction.CODEC); // Controller actions // TODO: @@ -42,70 +48,30 @@ public class AzActionRegistry { // TODO: } - public static @Nullable Class getActionClassOrNull(ResourceLocation resourceLocation) { + public static @Nullable > T getCodecOrNull( + ResourceLocation resourceLocation + ) { var id = RESOURCE_LOCATION_TO_ID.get(resourceLocation); - return id == null ? null : ACTION_CLASS_BY_ID.get(id); + @SuppressWarnings("unchecked") + var codec = (T) CODEC_BY_ID.get(id); + return codec; } - public static @Nullable Class getActionClassOrNull(short id) { - return ACTION_CLASS_BY_ID.get(id); + public static @Nullable > T getCodecOrNull(short id) { + @SuppressWarnings("unchecked") + var codec = (T) CODEC_BY_ID.get(id); + return codec; } public static @Nullable Short getIdOrNull(ResourceLocation resourceLocation) { return RESOURCE_LOCATION_TO_ID.get(resourceLocation); } - private static void register(ResourceLocation resourceLocation, Class clazz) { - Short id = RESOURCE_LOCATION_TO_ID.computeIfAbsent(resourceLocation, ($) -> NEXT_FREE_ID++); - ACTION_CLASS_BY_ID.put(id, clazz); + private static void register( + ResourceLocation resourceLocation, + StreamCodec codec + ) { + var id = RESOURCE_LOCATION_TO_ID.computeIfAbsent(resourceLocation, ($) -> NEXT_FREE_ID++); + CODEC_BY_ID.put(id, codec); } - - public static @Nullable AzAction decode(FriendlyByteBuf buf) { - var id = buf.readShort(); - var actionClass = getActionClassOrNull(id); - - if (actionClass == null) { - throw new NullPointerException( - "Could not find action class for a given action id while decoding data. ID: " + id - ); - } - - try { - // Use the static decode method in the corresponding action class - return (AzAction) actionClass.getDeclaredMethod("decode", FriendlyByteBuf.class).invoke(null, buf); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to decode action for ID: " + id, e); - } - } - - public static void encode(FriendlyByteBuf buf, AzAction action) { - var resourceLocation = action.getResourceLocation(); - var id = getIdOrNull(resourceLocation); - var actionClass = getActionClassOrNull(resourceLocation); - - if (id == null) { - throw new NullPointerException( - "Could not find action id for a given resource location while encoding data. Resource Location: " - + resourceLocation - ); - } - - if (actionClass == null) { - throw new NullPointerException( - "Could not find action class for a given resource location while encoding data. Resource Location: " - + resourceLocation + ", ID: " + id - ); - } - - buf.writeShort(id); - - try { - // Use the static encode method in the corresponding action class - actionClass.getDeclaredMethod("encode", FriendlyByteBuf.class, AzAction.class) - .invoke(null, buf, action); - } catch (ReflectiveOperationException e) { - throw new AzureLibException("Failed to encode action for Resource Location: " + resourceLocation, e); - } - } - } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java index 3163caa40..fb117fc24 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java @@ -1,6 +1,7 @@ package mod.azure.azurelib.rewrite.animation.dispatch.command.sequence; import mod.azure.azurelib.rewrite.animation.dispatch.command.stage.AzAnimationStage; +import mod.azure.azurelib.rewrite.util.codec.AzListStreamCodec; import net.minecraft.network.FriendlyByteBuf; import java.util.List; @@ -9,16 +10,9 @@ public record AzAnimationSequence( List stages ) { - public static void encode(FriendlyByteBuf buf, AzAnimationSequence sequence) { - List stages = sequence.stages(); - buf.writeInt(stages.size()); - for (AzAnimationStage stage : stages) { - stage.encode(buf, stage); - } - } - - public AzAnimationSequence decode(FriendlyByteBuf buf) { - return new AzAnimationSequence(stages); - } - + public static final StreamCodec CODEC = StreamCodec.composite( + new AzListStreamCodec<>(AzAnimationStage.CODEC), + AzAnimationSequence::stages, + AzAnimationSequence::new + ); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java index c22c6a24e..a3edbe88e 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java @@ -2,23 +2,18 @@ import mod.azure.azurelib.rewrite.animation.property.AzAnimationStageProperties; import net.minecraft.network.FriendlyByteBuf; -import org.jetbrains.annotations.NotNull; public record AzAnimationStage( - String name, - AzAnimationStageProperties properties + String name, + AzAnimationStageProperties properties ) { - public void encode(@NotNull FriendlyByteBuf buf, @NotNull AzAnimationStage stage) { - buf.writeUtf(stage.name()); - AzAnimationStageProperties.CODEC.encode(buf, stage.properties()); - } - - public AzAnimationStage decode(@NotNull FriendlyByteBuf buf) { - var name = buf.readUtf(); - var properties = AzAnimationStageProperties.CODEC.decode(buf); - - return new AzAnimationStage(name, properties); - } + public static final StreamCodec CODEC = StreamCodec.composite( + ByteBufCodecs.STRING_UTF8, + AzAnimationStage::name, + AzAnimationStageProperties.CODEC, + AzAnimationStage::properties, + AzAnimationStage::new + ); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java index 53520c351..fa7c3656b 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java @@ -13,17 +13,10 @@ public interface AzEasingType { Double2DoubleFunction buildTransformer(Double value); - static void encode(FriendlyByteBuf buf, AzEasingType value) { - buf.writeUtf(value.name()); - } - - static AzEasingType decode(FriendlyByteBuf buf) { - return Objects.requireNonNull( - AzEasingTypeRegistry.getOrNull(buf.readUtf()), - "Invalid or unknown AzEasingType received" - ); - } - + StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, val) -> buf.writeUtf(val.name()), + buf -> Objects.requireNonNull(AzEasingTypeRegistry.getOrNull(buf.readUtf())) + ); default double apply(AzAnimationPoint animationPoint) { Double easingVariable = null; diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java index b12380ecf..aa6877b8e 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java @@ -6,8 +6,9 @@ import net.minecraft.network.FriendlyByteBuf; import org.jetbrains.annotations.NotNull; -public class AzAnimationPropertiesCodec { +public class AzAnimationPropertiesCodec implements StreamCodec { + @Override public @NotNull AzAnimationProperties decode(FriendlyByteBuf buf) { var propertyLength = buf.readByte(); var properties = AzAnimationProperties.EMPTY; @@ -28,6 +29,7 @@ public class AzAnimationPropertiesCodec { return properties; } + @Override public void encode(FriendlyByteBuf buf, AzAnimationProperties properties) { var propertyLength = 0; propertyLength += properties.hasAnimationSpeed() ? 1 : 0; diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java index 044d47b96..1a4b76867 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java @@ -8,8 +8,9 @@ import net.minecraft.network.FriendlyByteBuf; import org.jetbrains.annotations.NotNull; -public class AzAnimationStagePropertiesCodec { +public class AzAnimationStagePropertiesCodec implements StreamCodec { + @Override public @NotNull AzAnimationStageProperties decode(FriendlyByteBuf buf) { var propertyLength = buf.readByte(); var properties = AzAnimationStageProperties.EMPTY; @@ -34,6 +35,7 @@ public class AzAnimationStagePropertiesCodec { return properties; } + @Override public void encode(FriendlyByteBuf buf, AzAnimationStageProperties properties) { var propertyLength = 0; propertyLength += properties.hasAnimationSpeed() ? 1 : 0; diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java index 85efd86de..e8872dd56 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java @@ -21,25 +21,31 @@ */ public class AzRendererConfig { - private final Supplier<@Nullable AzAnimator> animatorProvider; + protected final Supplier<@Nullable AzAnimator> animatorProvider; - private final Function modelLocationProvider; + protected final Function modelLocationProvider; - private final Function renderTypeFunction; + protected final Function renderTypeFunction; - private final List> renderLayers; + private final Function, AzRendererPipelineContext> preRenderEntry; - private final Function textureLocationProvider; + private final Function, AzRendererPipelineContext> postRenderEntry; - private final float scaleHeight; + protected final List> renderLayers; - private final float scaleWidth; + protected final Function textureLocationProvider; + + protected final float scaleHeight; + + protected final float scaleWidth; public AzRendererConfig( Supplier> animatorProvider, Function modelLocationProvider, Function renderTypeFunction, List> renderLayers, + Function, AzRendererPipelineContext> preRenderEntry, + Function, AzRendererPipelineContext> postRenderEntry, Function textureLocationProvider, float scaleHeight, float scaleWidth @@ -48,6 +54,8 @@ public AzRendererConfig( this.modelLocationProvider = modelLocationProvider; this.renderTypeFunction = renderTypeFunction; this.renderLayers = Collections.unmodifiableList(renderLayers); + this.preRenderEntry = preRenderEntry; + this.postRenderEntry = postRenderEntry; this.textureLocationProvider = textureLocationProvider; this.scaleHeight = scaleHeight; this.scaleWidth = scaleWidth; @@ -73,6 +81,14 @@ public List> renderLayers() { return renderLayers; } + public AzRendererPipelineContext preRenderEntry(AzRendererPipelineContext animatable) { + return preRenderEntry.apply(animatable); + } + + public AzRendererPipelineContext postRenderEntry(AzRendererPipelineContext animatable) { + return postRenderEntry.apply(animatable); + } + public float scaleHeight() { return scaleHeight; } @@ -89,6 +105,10 @@ public static class Builder { private final List> renderLayers; + private Function, AzRendererPipelineContext> preRenderEntry; + + private Function, AzRendererPipelineContext> postRenderEntry; + private final Function textureLocationProvider; private Supplier<@Nullable AzAnimator> animatorProvider; @@ -105,6 +125,8 @@ protected Builder( this.modelLocationProvider = modelLocationProvider; this.renderTypeProvider = $ -> RenderType.entityCutoutNoCull(textureLocationProvider.apply($)); this.renderLayers = new ObjectArrayList<>(); + this.preRenderEntry = $ -> $; + this.postRenderEntry = $ -> $; this.textureLocationProvider = textureLocationProvider; this.scaleHeight = 1; this.scaleWidth = 1; @@ -131,13 +153,13 @@ public Builder addRenderLayer(AzRenderLayer renderLayer) { return this; } - public Builder setRenderType(RenderType renderType) { - this.renderTypeProvider = $ -> renderType; + public Builder setPrerenderEntry(Function, AzRendererPipelineContext> preRenderEntry) { + this.preRenderEntry = preRenderEntry; return this; } - public Builder setRenderType(Function renderTypeProvider) { - this.renderTypeProvider = renderTypeProvider; + public Builder setPostRenderEntry(Function, AzRendererPipelineContext> postRenderEntry) { + this.postRenderEntry = postRenderEntry; return this; } @@ -177,6 +199,8 @@ public AzRendererConfig build() { modelLocationProvider, renderTypeProvider, renderLayers, + preRenderEntry, + postRenderEntry, textureLocationProvider, scaleHeight, scaleWidth diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java index 0d9fde789..25c4ec0a7 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java @@ -7,9 +7,6 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.Entity; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Objects; @@ -46,7 +43,7 @@ public abstract class AzRendererPipelineContext { private float alpha; - private @Nullable RenderType renderType; + private RenderType renderType; private VertexConsumer vertexConsumer; @@ -114,10 +111,10 @@ public void populate( * Uses the {@link RenderType#entityCutoutNoCull} {@code RenderType} by default.
* Override this to change the way a model will render (such as translucent models, etc) */ - public abstract @NotNull RenderType getDefaultRenderType( + public abstract RenderType getDefaultRenderType( T animatable, ResourceLocation texture, - @Nullable MultiBufferSource bufferSource, + MultiBufferSource bufferSource, float partialTick ); @@ -186,11 +183,11 @@ public PoseStack poseStack() { return poseStack; } - public @Nullable RenderType renderType() { + public RenderType renderType() { return renderType; } - public void setRenderType(@Nullable RenderType renderType) { + public void setRenderType(RenderType renderType) { this.renderType = renderType; } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java index c9ad35be2..112c8d163 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java @@ -2,6 +2,7 @@ import mod.azure.azurelib.rewrite.animation.AzAnimator; import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; import mod.azure.azurelib.rewrite.render.armor.bone.AzArmorBoneProvider; import mod.azure.azurelib.rewrite.render.armor.bone.AzDefaultArmorBoneProvider; import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; @@ -24,6 +25,8 @@ private AzArmorRendererConfig( Function modelLocationProvider, Function renderTypeProvider, List> renderLayers, + Function, AzRendererPipelineContext> preRenderEntry, + Function, AzRendererPipelineContext> postRenderEntry, Function textureLocationProvider, float scaleHeight, float scaleWidth @@ -33,6 +36,8 @@ private AzArmorRendererConfig( modelLocationProvider, renderTypeProvider, renderLayers, + preRenderEntry, + postRenderEntry, textureLocationProvider, scaleHeight, scaleWidth @@ -76,11 +81,26 @@ public Builder addRenderLayer(AzRenderLayer renderLayer) { return (Builder) super.addRenderLayer(renderLayer); } + public Builder setRenderType(RenderType renderType) { + this.renderTypeProvider = $ -> renderType; + return this; + } + + public Builder setRenderType(Function renderTypeProvider) { + this.renderTypeProvider = renderTypeProvider; + return this; + } + @Override public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animatorProvider) { return (Builder) super.setAnimatorProvider(animatorProvider); } + @Override + public Builder setPrerenderEntry(Function, AzRendererPipelineContext> preRenderEntry) { + return (Builder) super.setPrerenderEntry(preRenderEntry); + } + public Builder setBoneProvider(AzArmorBoneProvider boneProvider) { this.boneProvider = boneProvider; return this; @@ -96,6 +116,8 @@ public AzArmorRendererConfig build() { baseConfig::modelLocation, baseConfig::getRenderType, baseConfig.renderLayers(), + baseConfig::preRenderEntry, + baseConfig::postRenderEntry, baseConfig::textureLocation, baseConfig.scaleHeight(), baseConfig.scaleWidth() diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java index 9c942c7b5..21392acd6 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java @@ -72,10 +72,13 @@ public void preRender(AzRendererPipelineContext context, boolean isRe scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); boneContext.applyBoneVisibilityBySlot(currentSlot); + config.preRenderEntry(context); } @Override - public void postRender(AzRendererPipelineContext context, boolean isReRender) {} + public void postRender(AzRendererPipelineContext context, boolean isReRender) { + config.postRenderEntry(context); + } /** * Apply custom scaling to account for {@link net.minecraft.client.model.AgeableListModel AgeableListModel} baby diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityModelRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityModelRenderer.java index fc03b2d20..0552b5d8e 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityModelRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityModelRenderer.java @@ -48,9 +48,9 @@ public void render(AzRendererPipelineContext context, boolean isReRender) { var poseStack = context.poseStack(); if (!isReRender) { - rotateBlock(getFacing(entity), poseStack); poseStack.translate(0.5, 0, 0.5); + rotateBlock(getFacing(entity), poseStack); var animator = blockEntityRendererPipeline.getRenderer().getAnimator(); if (animator != null) { diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java index c9d26c437..35ff8b6aa 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java @@ -2,6 +2,7 @@ import mod.azure.azurelib.rewrite.animation.AzAnimator; import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; @@ -26,6 +27,8 @@ private AzBlockEntityRendererConfig( Function modelLocationProvider, Function renderTypeFunction, List> renderLayers, + Function, AzRendererPipelineContext> preRenderEntry, + Function, AzRendererPipelineContext> postRenderEntry, Function textureLocationProvider, float scaleHeight, float scaleWidth @@ -35,6 +38,8 @@ private AzBlockEntityRendererConfig( modelLocationProvider, renderTypeFunction, renderLayers, + preRenderEntry, + postRenderEntry, textureLocationProvider, scaleHeight, scaleWidth @@ -69,6 +74,21 @@ public Builder addRenderLayer(AzRenderLayer renderLayer) { return (Builder) super.addRenderLayer(renderLayer); } + public Builder setRenderType(RenderType renderType) { + this.renderTypeProvider = $ -> renderType; + return this; + } + + public Builder setRenderType(Function renderTypeProvider) { + this.renderTypeProvider = renderTypeProvider; + return this; + } + + @Override + public Builder setPrerenderEntry(Function, AzRendererPipelineContext> preRenderEntry) { + return (Builder) super.setPrerenderEntry(preRenderEntry); + } + @Override public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animatorProvider) { return (Builder) super.setAnimatorProvider(animatorProvider); @@ -83,6 +103,8 @@ public AzBlockEntityRendererConfig build() { baseConfig::modelLocation, baseConfig::getRenderType, baseConfig.renderLayers(), + baseConfig::preRenderEntry, + baseConfig::postRenderEntry, baseConfig::textureLocation, baseConfig.scaleHeight(), baseConfig.scaleWidth() diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java index b12169ee1..7ed9c24ad 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java @@ -75,10 +75,13 @@ public void preRender(AzRendererPipelineContext context, boolean isReRender) var scaleWidth = config.scaleWidth(); var scaleHeight = config.scaleHeight(); scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); + config.preRenderEntry(context); } @Override - public void postRender(AzRendererPipelineContext context, boolean isReRender) {} + public void postRender(AzRendererPipelineContext context, boolean isReRender) { + config.postRenderEntry(context); + } public AzBlockEntityRenderer getRenderer() { return blockEntityRenderer; diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java index 452318aa8..8387bf460 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java @@ -2,6 +2,7 @@ import mod.azure.azurelib.rewrite.animation.AzAnimator; import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; @@ -28,6 +29,8 @@ private AzEntityRendererConfig( Function modelLocationProvider, Function renderTypeFunction, List> renderLayers, + Function, AzRendererPipelineContext> preRenderEntry, + Function, AzRendererPipelineContext> postRenderEntry, Function textureLocationProvider, float scaleHeight, float scaleWidth @@ -37,6 +40,8 @@ private AzEntityRendererConfig( modelLocationProvider, renderTypeFunction, renderLayers, + preRenderEntry, + postRenderEntry, textureLocationProvider, scaleHeight, scaleWidth @@ -79,6 +84,30 @@ public Builder addRenderLayer(AzRenderLayer renderLayer) { return (Builder) super.addRenderLayer(renderLayer); } + public Builder setRenderType(RenderType renderType) { + this.renderTypeProvider = $ -> renderType; + return this; + } + + public Builder setRenderType(Function renderTypeProvider) { + this.renderTypeProvider = renderTypeProvider; + return this; + } + + @Override + public Builder setPrerenderEntry( + Function, AzRendererPipelineContext> preRenderEntry + ) { + return (Builder) super.setPrerenderEntry(preRenderEntry); + } + + @Override + public Builder setPostRenderEntry( + Function, AzRendererPipelineContext> preRenderEntry + ) { + return (Builder) super.setPostRenderEntry(preRenderEntry); + } + @Override public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animatorProvider) { return (Builder) super.setAnimatorProvider(animatorProvider); @@ -110,6 +139,8 @@ public AzEntityRendererConfig build() { baseConfig::modelLocation, baseConfig::getRenderType, baseConfig.renderLayers(), + baseConfig::preRenderEntry, + baseConfig::postRenderEntry, baseConfig::textureLocation, baseConfig.scaleHeight(), baseConfig.scaleWidth() diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java index 912664ff9..db87c9ea8 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java @@ -73,10 +73,13 @@ public void preRender(AzRendererPipelineContext context, boolean isReRender) var scaleHeight = config.scaleHeight(); scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); + config.preRenderEntry(context); } @Override - public void postRender(AzRendererPipelineContext context, boolean isReRender) {} + public void postRender(AzRendererPipelineContext context, boolean isReRender) { + config.postRenderEntry(context); + } /** * Renders the final frame of the entity, including handling special cases such as entities with leashes. diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java index daa8c8025..e928e0390 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java @@ -25,7 +25,7 @@ public abstract class AzItemRenderer { private final AzProvider provider; - private final AzItemRendererPipeline rendererPipeline; + public final AzItemRendererPipeline rendererPipeline; @Nullable private AzItemAnimator reusedAzItemAnimator; diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java index 7f7e20240..d61e9c62b 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java @@ -2,6 +2,7 @@ import mod.azure.azurelib.rewrite.animation.AzAnimator; import mod.azure.azurelib.rewrite.render.AzRendererConfig; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; @@ -28,6 +29,8 @@ private AzItemRendererConfig( Function modelLocationProvider, Function renderTypeProvider, List> renderLayers, + Function, AzRendererPipelineContext> preRenderEntry, + Function, AzRendererPipelineContext> postRenderEntry, Function textureLocationProvider, float scaleHeight, float scaleWidth, @@ -39,6 +42,8 @@ private AzItemRendererConfig( modelLocationProvider, renderTypeProvider, renderLayers, + preRenderEntry, + postRenderEntry, textureLocationProvider, scaleHeight, scaleWidth @@ -89,6 +94,30 @@ public Builder addRenderLayer(AzRenderLayer renderLayer) { return (Builder) super.addRenderLayer(renderLayer); } + public Builder setRenderType(RenderType renderType) { + this.renderTypeProvider = $ -> renderType; + return this; + } + + public Builder setRenderType(Function renderTypeProvider) { + this.renderTypeProvider = renderTypeProvider; + return this; + } + + @Override + public Builder setPrerenderEntry( + Function, AzRendererPipelineContext> preRenderEntry + ) { + return (Builder) super.setPrerenderEntry(preRenderEntry); + } + + @Override + public Builder setPostRenderEntry( + Function, AzRendererPipelineContext> preRenderEntry + ) { + return (Builder) super.setPostRenderEntry(preRenderEntry); + } + @Override public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animatorProvider) { return (Builder) super.setAnimatorProvider(animatorProvider); @@ -103,7 +132,7 @@ public Builder useEntityGuiLighting() { * @param useNewOffset Determines whether to apply the y offset for a model due to the change in BlockBench * 4.11. */ - public AzRendererConfig.Builder useNewOffset(boolean useNewOffset) { + public Builder useNewOffset(boolean useNewOffset) { this.useNewOffset = useNewOffset; return this; } @@ -117,6 +146,8 @@ public AzItemRendererConfig build() { baseConfig::modelLocation, baseConfig::getRenderType, baseConfig.renderLayers(), + baseConfig::preRenderEntry, + baseConfig::postRenderEntry, baseConfig::textureLocation, baseConfig.scaleHeight(), baseConfig.scaleWidth(), diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java index 9dee871ee..05171d359 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java @@ -64,10 +64,13 @@ public void preRender(AzRendererPipelineContext context, boolean isRe var useNewOffset = config.useNewOffset(); poseStack.translate(0.5f, useNewOffset ? 0.0f : 0.51f, 0.5f); } + config.preRenderEntry(context); } @Override - public void postRender(AzRendererPipelineContext context, boolean isReRender) {} + public void postRender(AzRendererPipelineContext context, boolean isReRender) { + config.postRenderEntry(context); + } /** * Update the current frame of a {@link AnimatableTexture potentially animated} texture used by this diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzArmorLayer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzArmorLayer.java index 03d632a8f..c0bf7db8d 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzArmorLayer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzArmorLayer.java @@ -15,11 +15,14 @@ import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.SkullBlockRenderer; +import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.ItemTags; +import net.minecraft.util.FastColor; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; @@ -96,7 +99,6 @@ public void render(AzRendererPipelineContext context) {} */ @Override public void renderForBone(AzRendererPipelineContext context, AzBone bone) { - var poseStack = context.poseStack(); var armorStack = getArmorItemForBone(context, bone); if (armorStack == null) { @@ -109,7 +111,7 @@ public void renderForBone(AzRendererPipelineContext context, AzBone bone) { ) { renderSkullAsArmor(context, bone, armorStack, skullBlock); } else { - renderArmor(context, bone, armorStack, poseStack); + renderArmor(context, bone, armorStack); } } @@ -121,51 +123,31 @@ public void renderForBone(AzRendererPipelineContext context, AzBone bone) { * rendering. * @param bone The specific bone of the model where the armor piece will be rendered. * @param armorStack The ItemStack representing the armor item to render. - * @param poseStack The matrix stack used to apply transformations during rendering. */ - private void renderArmor( + public void renderArmor( AzRendererPipelineContext context, AzBone bone, - ItemStack armorStack, - PoseStack poseStack + ItemStack armorStack ) { var slot = getEquipmentSlotForBone(context, bone, armorStack); - var renderer = getRendererForItem(armorStack); + var renderer = AzArmorRendererRegistry.getOrNull(armorStack.getItem()); var model = getModelForItem(armorStack, slot); - var modelPart = getModelPartForBone(context, model); + var modelPart = getModelPartForBone(context, bone, model); if (!modelPart.cubes.isEmpty()) { - poseStack.pushPose(); - poseStack.scale(-1, -1, 1); + context.poseStack().pushPose(); + context.poseStack().scale(-1, -1, 1); - if (renderer != null) { - var boneContext = renderer.rendererPipeline().context().boneContext(); + if (renderer != null) { prepModelPartForRender(context, bone, modelPart); - renderer.prepForRender(context.animatable(), armorStack, slot, model); - boneContext.applyBoneVisibilityByPart(slot, modelPart, model); - model.renderToBuffer( - poseStack, - null, - context.packedLight(), - context.packedOverlay(), - context.red(), - context.green(), - context.blue(), - context.alpha() - ); + renderAzArmorPiece(context, slot, armorStack, renderer, context.animatable(), model, modelPart); } else if (armorStack.getItem() instanceof ArmorItem) { prepModelPartForRender(context, bone, modelPart); - renderVanillaArmorPiece( - context, - bone, - slot, - armorStack, - modelPart - ); + renderVanillaArmorPiece(context, bone, slot, armorStack, modelPart); } - poseStack.popPose(); + context.poseStack().popPose(); } } @@ -196,7 +178,11 @@ private void renderArmor( * This is then transformed into position for the final render */ @NotNull - protected ModelPart getModelPartForBone(AzRendererPipelineContext context, HumanoidModel baseModel) { + protected ModelPart getModelPartForBone( + AzRendererPipelineContext context, + AzBone bone, + HumanoidModel baseModel + ) { return baseModel.body; } @@ -209,6 +195,24 @@ protected ItemStack getArmorItemForBone(AzRendererPipelineContext context, Az return null; } + protected void renderAzArmorPiece( + AzRendererPipelineContext context, + EquipmentSlot slot, + ItemStack armorStack, + AzArmorRenderer renderer, + LivingEntity entity, + HumanoidModel model, + ModelPart modelPart + ) { + var renderPipelines = renderer.rendererPipeline(); + var boneContext = renderPipelines.context().boneContext(); + var armorModel = renderPipelines.armorModel(); + + renderer.prepForRender(entity, armorStack, slot, model); + boneContext.applyBoneVisibilityByPart(slot, modelPart, model); + armorModel.renderToBuffer(context.poseStack(), null, context.packedLight(), OverlayTexture.NO_OVERLAY, context.red(), context.green(), context.blue(), context.alpha()); + } + /** * Renders an individual armor piece base on the given {@link AzBone} and {@link ItemStack} */ @@ -293,14 +297,14 @@ protected VertexConsumer getVanillaArmorBuffer( * Returns a cached instance of a base HumanoidModel that is used for rendering/modelling the provided * {@link ItemStack} */ - protected HumanoidModel getModelForItem(ItemStack stack, EquipmentSlot slot) { + protected HumanoidModel getModelForItem(ItemStack stack, EquipmentSlot slot) { var renderer = getRendererForItem(stack); if (renderer == null) { - return slot == EquipmentSlot.LEGS ? INNER_ARMOR_MODEL : OUTER_ARMOR_MODEL; + return (HumanoidModel) (slot == EquipmentSlot.LEGS ? INNER_ARMOR_MODEL : OUTER_ARMOR_MODEL); } - return renderer.rendererPipeline().armorModel(); + return (HumanoidModel) renderer.rendererPipeline().armorModel(); } /** diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzBlockAndItemLayer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzBlockAndItemLayer.java index 4cf7ed782..5f0f1deb8 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzBlockAndItemLayer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzBlockAndItemLayer.java @@ -1,8 +1,5 @@ package mod.azure.azurelib.rewrite.render.layer; -import mod.azure.azurelib.rewrite.model.AzBone; -import mod.azure.azurelib.rewrite.render.AzRendererPipeline; -import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; import mod.azure.azurelib.util.RenderUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.OverlayTexture; @@ -13,9 +10,13 @@ import java.util.function.Function; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.AzRendererPipeline; +import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; + /** - * A {@link AzRenderLayer} responsible for rendering {@link BlockState - * BlockStates} or {@link ItemStack ItemStacks} onto a specified {@link AzRendererPipeline}. + * A {@link AzRenderLayer} responsible for rendering {@link net.minecraft.world.level.block.state.BlockState + * BlockStates} or {@link net.minecraft.world.item.ItemStack ItemStacks} onto a specified {@link AzRendererPipeline}. * This layer handles the rendering of physical elements, such as blocks and items, associated with animation bones. */ public class AzBlockAndItemLayer implements AzRenderLayer { @@ -29,8 +30,8 @@ public AzBlockAndItemLayer() { } public AzBlockAndItemLayer( - Function itemStackProvider, - Function blockStateProvider + Function itemStackProvider, + Function blockStateProvider ) { super(); @@ -57,8 +58,9 @@ public void render(AzRendererPipelineContext context) {} */ @Override public void renderForBone(AzRendererPipelineContext context, AzBone bone) { - var stack = itemStackForBone(bone); - var blockState = blockStateForBone(bone); + var animatable = context.animatable(); + var stack = itemStackForBone(bone, animatable); + var blockState = blockStateForBone(bone, animatable); if (stack == null && blockState == null) return; @@ -67,10 +69,10 @@ public void renderForBone(AzRendererPipelineContext context, AzBone bone) { RenderUtils.translateAndRotateMatrixForBone(context.poseStack(), bone); if (stack != null) - renderItemForBone(context, bone, stack); + renderItemForBone(context, bone, stack, animatable); if (blockState != null) - renderBlockForBone(context, bone, blockState); + renderBlockForBone(context, bone, blockState, animatable); context.poseStack().popPose(); } @@ -82,7 +84,7 @@ public void renderForBone(AzRendererPipelineContext context, AzBone bone) { * @param bone the bone for which to retrieve the {@link ItemStack} * @return the {@link ItemStack} relevant to the specified bone, or {@code null} if none exists */ - public ItemStack itemStackForBone(AzBone bone) { + public ItemStack itemStackForBone(AzBone bone, T animatable) { return itemStackProvider.apply(bone); } @@ -93,7 +95,7 @@ public ItemStack itemStackForBone(AzBone bone) { * @param bone the bone for which to retrieve the {@link BlockState} * @return the {@link BlockState} relevant to the specified bone, or {@code null} if none exists */ - public BlockState blockStateForBone(AzBone bone) { + public BlockState blockStateForBone(AzBone bone, T animatable) { return blockStateProvider.apply(bone); } @@ -105,7 +107,7 @@ public BlockState blockStateForBone(AzBone bone) { * @param stack the {@link ItemStack} to render * @return the {@link ItemDisplayContext} to use for rendering */ - protected ItemDisplayContext getTransformTypeForStack(AzBone bone, ItemStack stack) { + protected ItemDisplayContext getTransformTypeForStack(AzBone bone, ItemStack stack, T animatable) { return ItemDisplayContext.NONE; } @@ -117,35 +119,40 @@ protected ItemDisplayContext getTransformTypeForStack(AzBone bone, ItemStack sta * @param bone the bone where the {@link ItemStack} will be rendered * @param itemStack the {@link ItemStack} to render */ - protected void renderItemForBone(AzRendererPipelineContext context, AzBone bone, ItemStack itemStack) { + protected void renderItemForBone( + AzRendererPipelineContext context, + AzBone bone, + ItemStack itemStack, + T animatable + ) { if (context.animatable() instanceof LivingEntity livingEntity) { Minecraft.getInstance() - .getItemRenderer() - .renderStatic( - livingEntity, - itemStack, - getTransformTypeForStack(bone, itemStack), - false, - context.poseStack(), - context.multiBufferSource(), - livingEntity.level(), - context.packedLight(), - context.packedOverlay(), - livingEntity.getId() - ); + .getItemRenderer() + .renderStatic( + livingEntity, + itemStack, + getTransformTypeForStack(bone, itemStack, animatable), + false, + context.poseStack(), + context.multiBufferSource(), + livingEntity.level(), + context.packedLight(), + context.packedOverlay(), + livingEntity.getId() + ); } else { Minecraft.getInstance() - .getItemRenderer() - .renderStatic( - itemStack, - getTransformTypeForStack(bone, itemStack), - context.packedLight(), - context.packedOverlay(), - context.poseStack(), - context.multiBufferSource(), - Minecraft.getInstance().level, - context.animatable().hashCode() - ); + .getItemRenderer() + .renderStatic( + itemStack, + getTransformTypeForStack(bone, itemStack, animatable), + context.packedLight(), + context.packedOverlay(), + context.poseStack(), + context.multiBufferSource(), + Minecraft.getInstance().level, + context.animatable().hashCode() + ); } } @@ -157,21 +164,26 @@ protected void renderItemForBone(AzRendererPipelineContext context, AzBone bo * @param bone the bone where the {@link BlockState} will be rendered * @param blockState the {@link BlockState} to render */ - protected void renderBlockForBone(AzRendererPipelineContext context, AzBone bone, BlockState blockState) { + protected void renderBlockForBone( + AzRendererPipelineContext context, + AzBone bone, + BlockState blockState, + T animatable + ) { context.poseStack().pushPose(); context.poseStack().translate(-0.25f, -0.25f, -0.25f); context.poseStack().scale(0.5f, 0.5f, 0.5f); Minecraft.getInstance() - .getBlockRenderer() - .renderSingleBlock( - blockState, - context.poseStack(), - context.multiBufferSource(), - context.packedLight(), - OverlayTexture.NO_OVERLAY - ); + .getBlockRenderer() + .renderSingleBlock( + blockState, + context.poseStack(), + context.multiBufferSource(), + context.packedLight(), + OverlayTexture.NO_OVERLAY + ); context.poseStack().popPose(); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/util/codec/AzListStreamCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/util/codec/AzListStreamCodec.java new file mode 100644 index 000000000..e5d25c888 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/rewrite/util/codec/AzListStreamCodec.java @@ -0,0 +1,34 @@ +package mod.azure.azurelib.rewrite.util.codec; + +import net.minecraft.network.FriendlyByteBuf; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class AzListStreamCodec implements StreamCodec> { + + private final StreamCodec codec; + + public AzListStreamCodec(StreamCodec codec) { + this.codec = codec; + } + + @Override + public @NotNull List decode(FriendlyByteBuf buf) { + var size = buf.readByte(); + var list = new ArrayList(size); + + for (int i = 0; i < size; i++) { + list.add(codec.decode(buf)); + } + + return list; + } + + @Override + public void encode(FriendlyByteBuf buf, List elements) { + buf.writeByte(elements.size()); + elements.forEach(element -> codec.encode(buf, element)); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/util/RenderUtils.java b/common/src/main/java/mod/azure/azurelib/util/RenderUtils.java index 85913d0b0..fba17c370 100644 --- a/common/src/main/java/mod/azure/azurelib/util/RenderUtils.java +++ b/common/src/main/java/mod/azure/azurelib/util/RenderUtils.java @@ -177,7 +177,10 @@ public static double getCurrentSystemTick() { } /** - * Returns the current time (in ticks) that the {@link org.lwjgl.glfw.GLFW GLFW} instance has been running. This is effectively a permanent timer that counts up since the game was launched. + * Calculates and retrieves the current game tick. + * The value is determined by multiplying the current rendering time by 20. + * + * @return The current tick as a double value. */ public static double getCurrentTick() { return Blaze3D.getTime() * 20d; diff --git a/common/src/main/resources/assets/azurelib/lang/es_es.json b/common/src/main/resources/assets/azurelib/lang/es_es.json new file mode 100644 index 000000000..d7d3d9ef2 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/lang/es_es.json @@ -0,0 +1,54 @@ +{ + "category.azurelib.binds": "Atajos de AzureLib", + "key.azurelib.reload": "Recargar Arma", + "key.azurelib.scope": "Apuntar con el Arma", + "key.azurelib.fire": "Disparo Alternativo del Arma", + "text.azurelib.value.true": "Verdadero", + "text.azurelib.value.false": "Falso", + "text.azurelib.value.edit": "Editar", + "text.azurelib.value.back": "Atrás", + "text.azurelib.value.revert.default": "Usar configuración predeterminada", + "text.azurelib.value.revert.default.dialog": "¿Estás seguro de que quieres restablecer la configuración a los valores predeterminados?", + "text.azurelib.value.revert.changes": "Deshacer cambios", + "text.azurelib.value.revert.changes.dialog": "¿Estás seguro de que quieres descartar los cambios que has realizado?", + "text.azurelib.value.add_element": "Añadir elemento", + "text.azurelib.screen.select_config": "Seleccionar configuración", + "text.azurelib.screen.dialog.confirm": "Confirmar", + "text.azurelib.screen.dialog.cancel": "Cancelar", + "text.azurelib.screen.color_dialog": "Seleccionar color", + "text.azurelib.screen.color.red": "Rojo: %s", + "text.azurelib.screen.color.green": "Verde: %s", + "text.azurelib.screen.color.blue": "Azul: %s", + "text.azurelib.screen.color.alpha": "Alfa: %s", + "text.azurelib.error.character_value_empty": "¡El campo de carácter no puede estar vacío!", + "text.azurelib.error.nan": "¡El valor '%s' no es un número válido!", + "text.azurelib.error.num_bounds": "¡El valor '%s' no está dentro del intervalo <%s;%s>!", + "text.azurelib.error.pattern_mismatch": "¡El valor '%s' no coincide con el patrón \\%s\\!", + "config.screen.azurelib": "Configuración de AzureLib", + "config.azurelib.option.useVanillaUseKey": "Activar si las armas AzureDooms usan la tecla de uso de Vanilla o personalizada", + "config.azurelib.option.bool": "Boolean de Prueba", + "config.azurelib.option.number": "Número de Prueba", + "config.azurelib.option.longNumber": "Número Long de Prueba", + "config.azurelib.option.floatNumber": "Número Float de Prueba", + "config.azurelib.option.doubleNumber": "Número Double de Prueba", + "config.azurelib.option.string": "String de Prueba", + "config.azurelib.option.color": "Color de Prueba", + "config.azurelib.option.color2": "Color ARGB de Prueba", + "config.azurelib.option.boolArray": "Array Booleanos de Prueba", + "config.azurelib.option.intArray": "Array de Enteros de Prueba", + "config.azurelib.option.longArray": "Array de Longs de Prueba", + "config.azurelib.option.floatArray": "Array de Floats de Prueba", + "config.azurelib.option.stringArray": "Array de Strings de Prueba", + "config.azurelib.option.testEnum": "Enum de Prueba", + "config.azurelib.option.nestedTest": "Anidado de Prueba", + "config.azurelib.option.testInt": "Entero de Prueba", + "config.azurelib.option.genericwarning": "Advertencia Genérica", + "config.azurelib.option.disableOptifineWarning": "Desactivar Advertencia de Optfine", + "header.azurelib.optifine": "Advertencia: Optifine presente en el cliente del juego", + "message.azurelib.optifine": "Advertencia: Optifine no es compatible con la mayoría de los mods, y puede causar desde pequeños artefactos visuales hasta cuelgues del juego. Se recomienda eliminar el mod para evitar estos problemas y otros. Esta advertencia se puede desactivar en la configuración de AzureLibs, sin embargo, usted acepta la responsabilidad por cualquier error o problema que encuentre. Si necesitabas Optifine por alguna razón, consulta la lista de alternativas de Optifine de PrismLauncher:", + "label.azurelib.open_mods_folder": "Abrir Carpeta de Mods", + "label.azurelib.optifine_alternatives": "Alternativas a Optifine", + "label.azurelib.proceed_anyway": "Proceder de todos modos", + "enchantment.azurelib.incendiaryenchantment": "Accesorio Incendiario", + "enchantment.azurelib.incendiaryenchantment.desc": "Permite que las balas disparadas prendan fuego a las criaturas" +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/lang/fr_fr.json b/common/src/main/resources/assets/azurelib/lang/fr_fr.json index 776f76956..a4b536393 100644 --- a/common/src/main/resources/assets/azurelib/lang/fr_fr.json +++ b/common/src/main/resources/assets/azurelib/lang/fr_fr.json @@ -1,4 +1,25 @@ { "category.azurelib.binds": "Liaisons AzureLib", - "key.azurelib.reload": "Recharger le pistolet/l'arme" + "key.azurelib.reload": "Recharger le pistolet/l'arme", + "text.azurelib.value.true": "Oui", + "text.azurelib.value.false": "Non", + "text.azurelib.value.edit": "Modifier", + "text.azurelib.value.back": "Retour", + "text.azurelib.value.revert.default": "Utiliser la configuration par défaut", + "text.azurelib.value.revert.default.dialog": "Êtes-vous sûr de vouloir réinitialiser la configuration avec les valeurs par défaut ?", + "text.azurelib.value.revert.changes": "Annuler les modifications", + "text.azurelib.value.revert.changes.dialog": "Êtes-vous sûr de vouloir abandonner les modifications que vous avez apportées ?", + "text.azurelib.value.add_element": "Ajouter un élément", + "text.azurelib.screen.select_config": "Sélectionner une configuration", + "text.azurelib.screen.dialog.confirm": "Confirmer", + "text.azurelib.screen.dialog.cancel": "Annuler", + "text.azurelib.screen.color_dialog": "Sélectionner une couleur", + "text.azurelib.screen.color.red": "Rouge : %s", + "text.azurelib.screen.color.green": "Vert : %s", + "text.azurelib.screen.color.blue": "Bleu : %s", + "text.azurelib.screen.color.alpha": "Alpha : %s", + "text.azurelib.error.character_value_empty": "Le champ de caractère ne peut pas être vide !", + "text.azurelib.error.nan": "La valeur '%s' n'est pas un nombre valide !", + "text.azurelib.error.num_bounds": "La valeur '%s' n'est pas comprise dans l'intervalle <%s;%s> !", + "text.azurelib.error.pattern_mismatch": "La valeur '%s' ne correspond pas au motif \\%s\\ !" } diff --git a/common/src/main/resources/assets/azurelib/lang/ru_ru.json b/common/src/main/resources/assets/azurelib/lang/ru_ru.json new file mode 100644 index 000000000..8b5055a3c --- /dev/null +++ b/common/src/main/resources/assets/azurelib/lang/ru_ru.json @@ -0,0 +1,54 @@ +{ + "category.azurelib.binds": "Привязки клавиш AzureLib", + "key.azurelib.reload": "Перезарядить оружие", + "key.azurelib.scope": "Прицелиться оружием", + "key.azurelib.fire": "Альтернативный огонь из оружия", + "text.azurelib.value.true": "Истина", + "text.azurelib.value.false": "Ложь", + "text.azurelib.value.edit": "Редактировать", + "text.azurelib.value.back": "Назад", + "text.azurelib.value.revert.default": "Использовать конфигурацию по умолчанию", + "text.azurelib.value.revert.default.dialog": "Вы уверены, что хотите вернуть настройки к значениям по умолчанию?", + "text.azurelib.value.revert.changes": "Откатить изменения", + "text.azurelib.value.revert.changes.dialog": "Вы уверены, что хотите отменить внесённые в данный момент изменения?", + "text.azurelib.value.add_element": "Добавить элемент", + "text.azurelib.screen.select_config": "Выбрать конфигурацию", + "text.azurelib.screen.dialog.confirm": "Подтвердить", + "text.azurelib.screen.dialog.cancel": "Отменить", + "text.azurelib.screen.color_dialog": "Выбрать цвет", + "text.azurelib.screen.color.red": "Красный: %s", + "text.azurelib.screen.color.green": "Зелёный: %s", + "text.azurelib.screen.color.blue": "Синий: %s", + "text.azurelib.screen.color.alpha": "Прозрачность: %s", + "text.azurelib.error.character_value_empty": "Поле символов не может быть пустым!", + "text.azurelib.error.nan": "Значение '%s' не является допустимым числом!", + "text.azurelib.error.num_bounds": "Значение '%s' не находится в интервале <%s;%s>!", + "text.azurelib.error.pattern_mismatch": "Значение '%s' не соответствует паттерну \\%s\\!", + "config.screen.azurelib": "Настройки AzureLib", + "config.azurelib.option.disableOptifineWarning": "Отключить экран предупреждения Optifine", + "config.azurelib.option.useVanillaUseKey": "Переключить, будут ли пушки AzureDooms использовать ванильные настрйоки клавиатуры или пользовательские", + "config.azurelib.option.bool": "Тесировать логическое значение", + "config.azurelib.option.number": "Тестировать число", + "config.azurelib.option.longNumber": "Тестировать длинное число", + "config.azurelib.option.floatNumber": "Тестировать число с плавающей запятой", + "config.azurelib.option.doubleNumber": "Тестировать вещественное число", + "config.azurelib.option.string": "Тестировать строку", + "config.azurelib.option.color": "Тестировать цвет", + "config.azurelib.option.color2": "Тестировать цвет ARGB", + "config.azurelib.option.boolArray": "Тестировать массив логических значений", + "config.azurelib.option.intArray": "Тестировать массив целочисленных", + "config.azurelib.option.longArray": "Тестировать массив длинных целочисленных", + "config.azurelib.option.floatArray": "Тестировать массив чисел с плавающей запятой", + "config.azurelib.option.stringArray": "Тестировать массив строк", + "config.azurelib.option.testEnum": "Тестировать перечислимый тип", + "config.azurelib.option.nestedTest": "Тестировать вложенный", + "config.azurelib.option.testInt": "Тестировать целое число", + "config.azurelib.option.genericwarning": "Общее предупреждение", + "header.azurelib.optifine": "Внимание: Optifine присутствует в игровом клиенте", + "message.azurelib.optifine": "Внимание: Optifine не поддерживается большинством модов, начиная от незначительных визуальных искажений и заканчивая сбоями в игре. Рекомендуется удалить мод, чтобы избежать этих и других проблем. Это предупреждение можно отключить в настройках AzureLibs, однако вы принимаете на себя ответственность за любые ошибки или неполадки, с которыми вы столкнётесь. Если вам по той или иной причине понадобился Optifine, ознакомьтесь со списком альтернатив Optifine в PrismLauncher:", + "label.azurelib.open_mods_folder": "Открыть папку модов", + "label.azurelib.optifine_alternatives": "Альтернативы Optifine", + "label.azurelib.proceed_anyway": "Продолить в любом случае", + "enchantment.azurelib.incendiaryenchantment": "Зажигательная привязанность", + "enchantment.azurelib.incendiaryenchantment.desc": "Позволяет стрелять пулями, поджигающими мобов" +} diff --git a/common/src/main/resources/assets/azurelib/lang/zh_cn.json b/common/src/main/resources/assets/azurelib/lang/zh_cn.json new file mode 100644 index 000000000..4c1638105 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/lang/zh_cn.json @@ -0,0 +1,55 @@ +{ + "category.azurelib.binds": "AzureLib 绑定", + "key.azurelib.reload": "重新装填枪械/武器", + "key.azurelib.scope": "武器瞄准", + "key.azurelib.fire": "武器替代射击", + "text.azurelib.value.true": "真", + "text.azurelib.value.false": "假", + "text.azurelib.value.edit": "编辑", + "text.azurelib.value.back": "返回", + "text.azurelib.value.revert.default": "使用默认配置", + "text.azurelib.value.revert.default.dialog": "你确定要将配置重置为默认值吗?", + "text.azurelib.value.revert.changes": "回滚更改", + "text.azurelib.value.revert.changes.dialog": "你确定要放弃你当前所做的更改吗?", + "text.azurelib.value.add_element": "添加元素", + "text.azurelib.screen.select_config": "选择配置", + "text.azurelib.screen.dialog.confirm": "确认", + "text.azurelib.screen.dialog.cancel": "取消", + "text.azurelib.screen.color_dialog": "选择颜色", + "text.azurelib.screen.color.red": "红色: %s", + "text.azurelib.screen.color.green": "绿色: %s", + "text.azurelib.screen.color.blue": "蓝色: %s", + "text.azurelib.screen.color.alpha": "透明度: %s", + "text.azurelib.error.character_value_empty": "字符字段不能为空!", + "text.azurelib.error.nan": "值 '%s' 不是一个有效的数字!", + "text.azurelib.error.num_bounds": "值 '%s' 不在区间 <%s;%s> 内!", + "text.azurelib.error.pattern_mismatch": "值 '%s' 与模式 \\%s\\ 不匹配!", + "config.screen.azurelib": "AzureLib 配置", + "config.azurelib.option.disableOptifineWarning": "禁用 Optfine 警告", + "config.azurelib.option.useVanillaUseKey": "切换 AzureDooms 枪械是否使用 Vanilla Use 键或自定义", + "config.azurelib.option.bool": "测试布尔值", + "config.azurelib.option.number": "测试数字", + "config.azurelib.option.longNumber": "测试长数字", + "config.azurelib.option.floatNumber": "测试浮点数", + "config.azurelib.option.doubleNumber": "测试双精度数", + "config.azurelib.option.string": "测试字符串", + "config.azurelib.option.color": "测试颜色", + "config.azurelib.option.color2": "测试颜色 ARGB", + "config.azurelib.option.boolArray": "测试布尔数组", + "config.azurelib.option.intArray": "测试整数数组", + "config.azurelib.option.longArray": "测试长数组", + "config.azurelib.option.floatArray": "测试浮点数组", + "config.azurelib.option.stringArray": "测试字符串数组", + "config.azurelib.option.testEnum": "测试枚举", + "config.azurelib.option.nestedTest": "测试嵌套", + "config.azurelib.option.testInt": "测试整数", + "config.azurelib.option.genericwarning": "通用警告", + "header.azurelib.optifine": "警告: 游戏客户端存在 Optifine", + "message.azurelib.optifine": "警告: Optifine 未被大多数模组支持,可能会导致从轻微视觉错误到游戏崩溃等问题。建议移除该模组以避免这些问题及其他问题。此警告可在 AzureLibs 配置中禁用,但你需对遇到的任何错误或问题负责。如果你因某种原因需要 Optifine,可查看 PrismLauncher 的 Optifine 替代列表:", + "label.azurelib.open_mods_folder": "打开模组文件夹", + "label.azurelib.optifine_alternatives": "Optifine 替代品", + "label.azurelib.proceed_anyway": "仍然继续", + "enchantment.azurelib.incendiaryenchantment": "燃烧附件", + "enchantment.azurelib.incendiaryenchantment.desc": "使发射的子弹点燃目标" + } + diff --git a/fabric/src/main/java/mod/azure/azurelib/platform/FabricAzureLibNetwork.java b/fabric/src/main/java/mod/azure/azurelib/platform/FabricAzureLibNetwork.java index a0871a9ed..7c9a78395 100644 --- a/fabric/src/main/java/mod/azure/azurelib/platform/FabricAzureLibNetwork.java +++ b/fabric/src/main/java/mod/azure/azurelib/platform/FabricAzureLibNetwork.java @@ -3,13 +3,7 @@ import mod.azure.azurelib.network.AbstractPacket; import mod.azure.azurelib.network.Networking; import mod.azure.azurelib.network.S2C_SendConfigData; -import mod.azure.azurelib.network.packet.AnimDataSyncPacket; -import mod.azure.azurelib.network.packet.AnimTriggerPacket; -import mod.azure.azurelib.network.packet.BlockEntityAnimDataSyncPacket; -import mod.azure.azurelib.network.packet.BlockEntityAnimTriggerPacket; -import mod.azure.azurelib.network.packet.EntityAnimDataSyncPacket; -import mod.azure.azurelib.network.packet.EntityAnimTriggerPacket; -import mod.azure.azurelib.network.packet.EntityPacketOnClient; +import mod.azure.azurelib.network.packet.*; import mod.azure.azurelib.platform.services.AzureLibNetwork; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; @@ -44,6 +38,16 @@ public void registerClientReceiverPackets() { ClientPlayNetworking.registerGlobalReceiver(BLOCK_ENTITY_ANIM_TRIGGER_SYNC_PACKET_ID, (client, $2, buf, $4) -> this.handlePacket(client, BlockEntityAnimTriggerPacket.receive(buf))); ClientPlayNetworking.registerGlobalReceiver(CUSTOM_ENTITY_ID, (client, handler, buf, responseSender) -> EntityPacketOnClient.onPacket(client, buf)); + + ClientPlayNetworking.registerGlobalReceiver(AZ_ENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID, + (client, $2, buf, $4) -> + this.handlePacket(client, AzEntityDispatchCommandPacket.receive(buf))); + ClientPlayNetworking.registerGlobalReceiver(AZ_ITEM_STACK_DISPATCH_COMMAND_SYNC_PACKET_ID, + (client, $2, buf, $4) -> + this.handlePacket(client, AzItemStackDispatchCommandPacket.receive(buf))); + ClientPlayNetworking.registerGlobalReceiver(AZ_BLOCKENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID, + (client, $2, buf, $4) -> + this.handlePacket(client, AzBlockEntityDispatchCommandPacket.receive(buf))); } @Override diff --git a/neoforge/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java b/neoforge/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java index 7755fddc8..41fd08f6a 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java +++ b/neoforge/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java @@ -2,6 +2,7 @@ import mod.azure.azurelib.animatable.GeoItem; import mod.azure.azurelib.animatable.client.RenderProvider; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.model.Model; import net.minecraft.world.entity.EquipmentSlot; @@ -19,6 +20,17 @@ public class ClientHooksMixin { @Inject(method = "getArmorModel", at = @At("RETURN"), remap = false, cancellable = true) private static void injectAzureArmors(LivingEntity entityLiving, ItemStack itemStack, EquipmentSlot slot, HumanoidModel _default, CallbackInfoReturnable cir) { if (itemStack.getItem() instanceof GeoItem) - cir.setReturnValue((Model) RenderProvider.of(itemStack).getGenericArmorModel(entityLiving, itemStack, slot, (HumanoidModel) _default)); + cir.setReturnValue( + RenderProvider.of(itemStack) + .getGenericArmorModel(entityLiving, itemStack, slot, (HumanoidModel) _default) + ); + + var renderer = AzArmorRendererRegistry.getOrNull(itemStack.getItem()); + + if (renderer != null) { + var rendererPipeline = renderer.rendererPipeline(); + var armorModel = rendererPipeline.armorModel(); + cir.setReturnValue(armorModel); + } } } diff --git a/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java b/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java index c2595d6e8..45673a397 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java +++ b/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java @@ -46,6 +46,10 @@ public void registerClientReceiverPackets() { PACKET_CHANNEL.registerMessage(id++, EntityAnimTriggerPacket.class, EntityAnimTriggerPacket::encode, EntityAnimTriggerPacket::receive, this::handlePacket); PACKET_CHANNEL.registerMessage(id++, BlockEntityAnimDataSyncPacket.class, BlockEntityAnimDataSyncPacket::encode, BlockEntityAnimDataSyncPacket::receive, this::handlePacket); PACKET_CHANNEL.registerMessage(id++, BlockEntityAnimTriggerPacket.class, BlockEntityAnimTriggerPacket::encode, BlockEntityAnimTriggerPacket::receive, this::handlePacket); + + PACKET_CHANNEL.registerMessage(id++, AzBlockEntityDispatchCommandPacket.class, AzBlockEntityDispatchCommandPacket::encode, AzBlockEntityDispatchCommandPacket::receive, this::handlePacket); + PACKET_CHANNEL.registerMessage(id++, AzEntityDispatchCommandPacket.class, AzEntityDispatchCommandPacket::encode, AzEntityDispatchCommandPacket::receive, this::handlePacket); + PACKET_CHANNEL.registerMessage(id++, AzItemStackDispatchCommandPacket.class, AzItemStackDispatchCommandPacket::encode, AzItemStackDispatchCommandPacket::receive, this::handlePacket); } @Override From 1934d611c7547cd988fee5a507453e265c584af0 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:50:14 -0400 Subject: [PATCH 03/40] Update changelog.txt --- changelog.txt | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index cd8206488..c369aac3f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,19 @@ -v2.0.41 - -- Revert GeoGlowingTextureMeta change. \ No newline at end of file +TODO for 3.x port +- mod.azure.azurelib.network.packet.AzBlockEntityDispatchCommandPacket +- mod.azure.azurelib.network.packet.AzEntityDispatchCommandPacket +- mod.azure.azurelib.network.packet.AzItemStackDispatchCommandPacket +- mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide +- mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand +- mod.azure.azurelib.rewrite.animation.dispatch.command.action.codec.AzActionCodec +- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootCancelAction +- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootPlayAnimationSequenceAction +- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootSetAnimationSpeedAction +- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootSetEasingTypeAction +- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootSetTransitionSpeedAction +- mod.azure.azurelib.rewrite.animation.dispatch.command.action.registry.AzActionRegistry +- mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequence +- mod.azure.azurelib.rewrite.animation.dispatch.command.stage.AzAnimationStage +- mod.azure.azurelib.rewrite.animation.easing.AzEasingType +- mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationPropertiesCodec +- mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationStagePropertiesCodec +- mod.azure.azurelib.rewrite.util.codec.AzListStreamCodec \ No newline at end of file From bc59371ada441c96b78fb05f154c0beccaf64478 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:51:13 -0400 Subject: [PATCH 04/40] Move changelog to markdown --- changelog.txt => changelog.md | 0 fabric/build.gradle | 2 +- neoforge/build.gradle | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename changelog.txt => changelog.md (100%) diff --git a/changelog.txt b/changelog.md similarity index 100% rename from changelog.txt rename to changelog.md diff --git a/fabric/build.gradle b/fabric/build.gradle index 925e053cc..2c0ee03c1 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -126,7 +126,7 @@ if (file('key.properties').exists()) { File secretPropsFile = file("key.properties") releaseProp.load(secretPropsFile.newInputStream()) file = remapJar.archiveFile - changelog = rootProject.file("changelog.txt").text + changelog = rootProject.file("changelog.md").text type = STABLE modLoaders.add("fabric") modLoaders.add("quilt") diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 79afb023d..99c69d050 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -138,7 +138,7 @@ if (file('key.properties').exists()) { File secretPropsFile = file("key.properties") releaseProp.load(secretPropsFile.newInputStream()) file = jar.archiveFile - changelog = rootProject.file("changelog.txt").text + changelog = rootProject.file("changelog.md").text type = STABLE modLoaders.add("neoforge") modLoaders.add("forge") From 2c7bff80746780ad06628bf854a86dfb38da9448 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:54:25 -0400 Subject: [PATCH 05/40] Update AzCommand.java --- .../azurelib/rewrite/animation/dispatch/command/AzCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java index 3b59f78ee..9cb377df4 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java @@ -132,7 +132,7 @@ public void sendForItem(Entity entity, ItemStack itemStack) { if (entity.level().isClientSide()) { dispatchFromClient(entity); } else { - var uuid = itemStack.get(AzureLib.AZ_ID.get()); + var uuid = itemStack.getTag().getUUID("az_id"); if (uuid == null) { AzureLib.LOGGER.warn( From beb1ec40573219e40ea9387601754da07769ac64 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:06:01 -0400 Subject: [PATCH 06/40] Found missing class that needs backported as well. --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index c369aac3f..adf412801 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ TODO for 3.x port - mod.azure.azurelib.network.packet.AzItemStackDispatchCommandPacket - mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide - mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand +- mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction - mod.azure.azurelib.rewrite.animation.dispatch.command.action.codec.AzActionCodec - mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootCancelAction - mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootPlayAnimationSequenceAction From e12b6bca25193931c2e9495dc5afc1d6d386c782 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:14:11 -0400 Subject: [PATCH 07/40] Update changelog.md --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index adf412801..2c9b4435b 100644 --- a/changelog.md +++ b/changelog.md @@ -15,6 +15,8 @@ TODO for 3.x port - mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequence - mod.azure.azurelib.rewrite.animation.dispatch.command.stage.AzAnimationStage - mod.azure.azurelib.rewrite.animation.easing.AzEasingType +- mod.azure.azurelib.rewrite.animation.property.AzAnimationProperties +- mod.azure.azurelib.rewrite.animation.property.AzAnimationStageProperties - mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationPropertiesCodec - mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationStagePropertiesCodec - mod.azure.azurelib.rewrite.util.codec.AzListStreamCodec \ No newline at end of file From 81a4eac30553a4efeeb4ea0be3cedc0d5e44a9e9 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:58:41 -0400 Subject: [PATCH 08/40] Adds examples for testing, will be removed by release. --- .../azure/azurelib/testing/CommonStrings.java | 27 + .../testing/armor/DoomArmorBoneProvider.java | 28 + .../testing/armor/DoomicornArmor.java | 40 + .../DoomicornArmorAnimationDispatcher.java | 18 + .../testing/armor/DoomicornArmorAnimator.java | 30 + .../testing/armor/DoomicornArmorRenderer.java | 24 + .../entity/MarauderAnimationDispatcher.java | 81 + .../testing/entity/MarauderAnimator.java | 108 + .../testing/entity/MarauderEntity.java | 103 + .../testing/entity/MarauderRenderer.java | 26 + .../entity/ai/DelayedMeleeAttackGoal.java | 179 + .../item/PistolAnimationDispatcher.java | 20 + .../azurelib/testing/item/PistolAnimator.java | 34 + .../azurelib/testing/item/PistolItem.java | 45 + .../azurelib/testing/item/PistolRenderer.java | 21 + .../animations/block/stargate.animation.json | 249 + .../animations/entity/marauder.animation.json | 6262 ++++++ .../animations/item/doomicorn.animation.json | 741 + .../animations/item/pistol.animation.json | 80 + .../azurelib/geo/block/stargate.geo.json | 877 + .../azurelib/geo/entity/marauder.geo.json | 4168 ++++ .../azurelib/geo/item/doomicorn.geo.json | 17113 ++++++++++++++++ .../assets/azurelib/geo/item/pistol.geo.json | 3690 ++++ .../azurelib/models/block/stargate.json | 99 + .../azurelib/models/item/doomicorn_boots.json | 6 + .../models/item/doomicorn_chestplate.json | 6 + .../models/item/doomicorn_helmet.json | 6 + .../models/item/doomicorn_leggings.json | 6 + .../assets/azurelib/models/item/pistol.json | 96 + .../assets/azurelib/models/item/stargate.json | 96 + .../azurelib/textures/block/stargate.png | Bin 0 -> 8667 bytes .../azurelib/textures/entity/marauder.png | Bin 0 -> 11573 bytes .../textures/entity/marauder_glowmask.png | Bin 0 -> 8489 bytes .../azurelib/textures/item/doomicorn.png | Bin 0 -> 1189 bytes .../textures/item/doomicorn_boots.png | Bin 0 -> 136 bytes .../textures/item/doomicorn_chestplate.png | Bin 0 -> 338 bytes .../textures/item/doomicorn_helmet.png | Bin 0 -> 503 bytes .../textures/item/doomicorn_leggings.png | Bin 0 -> 139 bytes .../assets/azurelib/textures/item/pistol.png | Bin 0 -> 1486 bytes .../mod/azure/azurelib/ClientListener.java | 26 + .../mod/azure/azurelib/FabricAzureLibMod.java | 54 +- .../azurelib/testing/block/StargateBlock.java | 53 + .../be/StargateBlockAnimationDispatcher.java | 26 + .../testing/block/be/StargateBlockEntity.java | 32 + .../block/be/StargateBlockEntityAnimator.java | 38 + .../block/be/StargateBlockRenderer.java | 21 + .../mod/azure/azurelib/ClientModListener.java | 29 + .../azure/azurelib/NeoForgeAzureLibMod.java | 52 +- .../azurelib/testing/block/StargateBlock.java | 52 + .../be/StargateBlockAnimationDispatcher.java | 26 + .../testing/block/be/StargateBlockEntity.java | 32 + .../block/be/StargateBlockEntityAnimator.java | 39 + .../block/be/StargateBlockRenderer.java | 21 + 53 files changed, 34778 insertions(+), 2 deletions(-) create mode 100644 common/src/main/java/mod/azure/azurelib/testing/CommonStrings.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomArmorBoneProvider.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmor.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimationDispatcher.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimator.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimationDispatcher.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimator.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/MarauderEntity.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/ai/DelayedMeleeAttackGoal.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimationDispatcher.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimator.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java create mode 100644 common/src/main/java/mod/azure/azurelib/testing/item/PistolRenderer.java create mode 100644 common/src/main/resources/assets/azurelib/animations/block/stargate.animation.json create mode 100644 common/src/main/resources/assets/azurelib/animations/entity/marauder.animation.json create mode 100644 common/src/main/resources/assets/azurelib/animations/item/doomicorn.animation.json create mode 100644 common/src/main/resources/assets/azurelib/animations/item/pistol.animation.json create mode 100644 common/src/main/resources/assets/azurelib/geo/block/stargate.geo.json create mode 100644 common/src/main/resources/assets/azurelib/geo/entity/marauder.geo.json create mode 100644 common/src/main/resources/assets/azurelib/geo/item/doomicorn.geo.json create mode 100644 common/src/main/resources/assets/azurelib/geo/item/pistol.geo.json create mode 100644 common/src/main/resources/assets/azurelib/models/block/stargate.json create mode 100644 common/src/main/resources/assets/azurelib/models/item/doomicorn_boots.json create mode 100644 common/src/main/resources/assets/azurelib/models/item/doomicorn_chestplate.json create mode 100644 common/src/main/resources/assets/azurelib/models/item/doomicorn_helmet.json create mode 100644 common/src/main/resources/assets/azurelib/models/item/doomicorn_leggings.json create mode 100644 common/src/main/resources/assets/azurelib/models/item/pistol.json create mode 100644 common/src/main/resources/assets/azurelib/models/item/stargate.json create mode 100644 common/src/main/resources/assets/azurelib/textures/block/stargate.png create mode 100644 common/src/main/resources/assets/azurelib/textures/entity/marauder.png create mode 100644 common/src/main/resources/assets/azurelib/textures/entity/marauder_glowmask.png create mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn.png create mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn_boots.png create mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn_chestplate.png create mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn_helmet.png create mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn_leggings.png create mode 100644 common/src/main/resources/assets/azurelib/textures/item/pistol.png create mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java create mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java create mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java create mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java create mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java create mode 100644 neoforge/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java create mode 100644 neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java create mode 100644 neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java create mode 100644 neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java create mode 100644 neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java diff --git a/common/src/main/java/mod/azure/azurelib/testing/CommonStrings.java b/common/src/main/java/mod/azure/azurelib/testing/CommonStrings.java new file mode 100644 index 000000000..39de52fe2 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/CommonStrings.java @@ -0,0 +1,27 @@ +package mod.azure.azurelib.testing; + +public record CommonStrings() { + public static final String MOD_ID = "azexamples"; + + public static final String CREATIVE_TAB = "itemGroup." + CommonStrings.MOD_ID + ".examplemod_items"; + + public static final String BASE_CONTROLLER = "base_controller"; + + public static final String IDLE_ANIMATION_NAME = "idle"; + + public static final String WALK_ANIMATION_NAME = "walk"; + + public static final String SPAWN_ANIMATION_NAME = "spawn"; + + public static final String DEATH_ANIMATION_NAME = "death"; + + public static final String RUN_ANIMATION_NAME = "run"; + + public static final String MELEE_ANIMATION_NAME = "axe_attack"; + + public static final String SPIN_ANIMATION_NAME = "spinning"; + + public static final String FIRING_ANIMATION_NAME = "firing"; + + public static final String EQUIP_ANIMATION_NAME = "equipping"; +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomArmorBoneProvider.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomArmorBoneProvider.java new file mode 100644 index 000000000..4a94f0af7 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomArmorBoneProvider.java @@ -0,0 +1,28 @@ +package mod.azure.azurelib.testing.armor; + +import mod.azure.azurelib.rewrite.model.AzBakedModel; +import mod.azure.azurelib.rewrite.model.AzBone; +import mod.azure.azurelib.rewrite.render.armor.bone.AzDefaultArmorBoneProvider; + +public class DoomArmorBoneProvider extends AzDefaultArmorBoneProvider { + + @Override + public AzBone getLeftBootBone(AzBakedModel model) { + return model.getBone("armorRightBoot").orElse(null); + } + + @Override + public AzBone getLeftLegBone(AzBakedModel model) { + return model.getBone("armorRightLeg").orElse(null); + } + + @Override + public AzBone getRightBootBone(AzBakedModel model) { + return model.getBone("armorLeftBoot").orElse(null); + } + + @Override + public AzBone getRightLegBone(AzBakedModel model) { + return model.getBone("armorLeftLeg").orElse(null); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmor.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmor.java new file mode 100644 index 000000000..b9a9f6f6b --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmor.java @@ -0,0 +1,40 @@ +package mod.azure.azurelib.testing.armor; + +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.ArmorMaterials; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +public class DoomicornArmor extends ArmorItem { + + private final DoomicornArmorAnimationDispatcher dispatcher; + + public DoomicornArmor(Type type) { + super(ArmorMaterials.NETHERITE, type, new Properties().stacksTo(1)); + this.dispatcher = new DoomicornArmorAnimationDispatcher(); + } + + @Override + public @NotNull InteractionResultHolder swapWithEquipmentSlot( + @NotNull Item item, + @NotNull Level level, + @NotNull Player player, + @NotNull InteractionHand hand + ) { + InteractionResultHolder result = super.swapWithEquipmentSlot(item, level, player, hand); + + if (!level.isClientSide) { + EquipmentSlot slot = getEquipmentSlot(); + ItemStack itemStack = player.getItemBySlot(slot); + dispatcher.serverEquipHelmet(player, itemStack); + } + + return result; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimationDispatcher.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimationDispatcher.java new file mode 100644 index 000000000..208c99432 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimationDispatcher.java @@ -0,0 +1,18 @@ +package mod.azure.azurelib.testing.armor; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; +import mod.azure.azurelib.testing.CommonStrings; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; + +public class DoomicornArmorAnimationDispatcher { + + private static final AzCommand EQUIP = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.EQUIP_ANIMATION_NAME + ); + + public void serverEquipHelmet(Entity entity, ItemStack itemStack) { + EQUIP.sendForItem(entity, itemStack); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimator.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimator.java new file mode 100644 index 000000000..f8e013b64 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimator.java @@ -0,0 +1,30 @@ +package mod.azure.azurelib.testing.armor; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; +import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; +import mod.azure.azurelib.testing.CommonStrings; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class DoomicornArmorAnimator extends AzItemAnimator { + + private static final ResourceLocation ANIMATIONS = AzureLib.modResource( + "animations/item/doomicorn.animation.json" + ); + + @Override + public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { + animationControllerContainer.add( + AzAnimationController.builder(this, CommonStrings.BASE_CONTROLLER) + .build() + ); + } + + @Override + public @NotNull ResourceLocation getAnimationLocation(ItemStack animatable) { + return ANIMATIONS; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorRenderer.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorRenderer.java new file mode 100644 index 000000000..62acec796 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorRenderer.java @@ -0,0 +1,24 @@ +package mod.azure.azurelib.testing.armor; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRenderer; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererConfig; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; + +public class DoomicornArmorRenderer extends AzArmorRenderer { + + private static final ResourceLocation MODEL = AzureLib.modResource("geo/item/doomicorn.geo.json"); + + private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/item/doomicorn.png"); + + public DoomicornArmorRenderer() { + super( + AzArmorRendererConfig.builder(MODEL, TEXTURE) + .setAnimatorProvider(DoomicornArmorAnimator::new) + .setBoneProvider(new DoomArmorBoneProvider()) + .setRenderType(RenderType.entityTranslucent(TEXTURE)) + .build() + ); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimationDispatcher.java b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimationDispatcher.java new file mode 100644 index 000000000..019d7fbb1 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimationDispatcher.java @@ -0,0 +1,81 @@ +package mod.azure.azurelib.testing.entity; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; +import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviors; +import mod.azure.azurelib.testing.CommonStrings; + +/** + * The MarauderAnimationDispatcher class is responsible for managing and dispatching animation commands for entities, + * specifically tailored for the Marauder entity. It uses predefined animation commands to sync animations with specific + * entity actions such as idling, walking, running, melee attacking, spawning, or dying. This dispatcher operates + * independently, providing a clean and centralized way to handle animation transitions, triggered by client or server + * events. + */ +public class MarauderAnimationDispatcher { + + private final AzCommand deathCommand = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.DEATH_ANIMATION_NAME, + AzPlayBehaviors.HOLD_ON_LAST_FRAME + ); + + private final AzCommand idleCommand = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.IDLE_ANIMATION_NAME, + AzPlayBehaviors.LOOP + ); + + private final AzCommand walkCommand = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.WALK_ANIMATION_NAME, + AzPlayBehaviors.LOOP + ); + + private final AzCommand runCommand = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.RUN_ANIMATION_NAME, + AzPlayBehaviors.LOOP + ); + + private final AzCommand meleeCommand = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.MELEE_ANIMATION_NAME, + AzPlayBehaviors.PLAY_ONCE + ); + + private final AzCommand spawnCommand = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.SPAWN_ANIMATION_NAME, + AzPlayBehaviors.PLAY_ONCE + ); + + private final MarauderEntity marauder; + + public MarauderAnimationDispatcher(MarauderEntity marauder) { + this.marauder = marauder; + } + + public void clientIdle() { + idleCommand.sendForEntity(marauder); + } + + public void clientWalk() { + walkCommand.sendForEntity(marauder); + } + + public void clientRun() { + runCommand.sendForEntity(marauder); + } + + public void serverMelee() { + meleeCommand.sendForEntity(marauder); + } + + public void clientDeath() { + deathCommand.sendForEntity(marauder); + } + + public void clientSpawn() { + spawnCommand.sendForEntity(marauder); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimator.java b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimator.java new file mode 100644 index 000000000..d0f1f6145 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimator.java @@ -0,0 +1,108 @@ +package mod.azure.azurelib.testing.entity; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeCallbacks; +import mod.azure.azurelib.rewrite.animation.impl.AzEntityAnimator; +import mod.azure.azurelib.testing.CommonStrings; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import org.jetbrains.annotations.NotNull; + +/** + * The {@code MarauderAnimator} class is responsible for controlling the animations of a {@link MarauderEntity}. It + * defines the animation workflows for various states such as idle, walking, running, spawning, attacking, and dying, + * and binds these animations to the corresponding keyframe events. This class extends the {@code AzEntityAnimator} + * framework, providing an implementation specific to the {@code MarauderEntity}. + */ +public class MarauderAnimator extends AzEntityAnimator { + + private static final ResourceLocation ANIMATIONS = AzureLib.modResource( + "animations/entity/marauder.animation.json" + ); + + public MarauderAnimator() { + super(AzAnimatorConfig.defaultConfig()); + } + + @Override + public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { + animationControllerContainer.add( + AzAnimationController.builder(this, CommonStrings.BASE_CONTROLLER) + .setTransitionLength(0) + .setKeyframeCallbacks( + AzKeyframeCallbacks.builder() + .setSoundKeyframeHandler( + event -> { + if (event.getKeyframeData().getSound().equals("walk")) { + event.getAnimatable() + .level() + .playLocalSound( + event.getAnimatable().getX(), + event.getAnimatable().getY(), + event.getAnimatable().getZ(), + SoundEvents.METAL_STEP, + SoundSource.HOSTILE, + 1.00F, + 1.0F, + true + ); + } + if (event.getKeyframeData().getSound().equals("run")) { + event.getAnimatable() + .level() + .playLocalSound( + event.getAnimatable().getX(), + event.getAnimatable().getY(), + event.getAnimatable().getZ(), + SoundEvents.SKELETON_STEP, + SoundSource.HOSTILE, + 1.00F, + 1.0F, + true + ); + } + if (event.getKeyframeData().getSound().equals("portal")) { + event.getAnimatable() + .level() + .playLocalSound( + event.getAnimatable().getX(), + event.getAnimatable().getY(), + event.getAnimatable().getZ(), + SoundEvents.PORTAL_AMBIENT, + SoundSource.HOSTILE, + 0.20F, + 1.0F, + true + ); + } + if (event.getKeyframeData().getSound().equals("axe")) { + event.getAnimatable() + .level() + .playLocalSound( + event.getAnimatable().getX(), + event.getAnimatable().getY(), + event.getAnimatable().getZ(), + SoundEvents.ENDER_EYE_LAUNCH, + SoundSource.HOSTILE, + 1.00F, + 1.0F, + true + ); + } + } + ) + .build() + ) + .build() + ); + } + + @Override + public @NotNull ResourceLocation getAnimationLocation(MarauderEntity drone) { + return ANIMATIONS; + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderEntity.java b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderEntity.java new file mode 100644 index 000000000..16f8b39e4 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderEntity.java @@ -0,0 +1,103 @@ +package mod.azure.azurelib.testing.entity; + +import mod.azure.azurelib.ai.pathing.AzureNavigation; +import mod.azure.azurelib.rewrite.util.MoveAnalysis; +import mod.azure.azurelib.testing.entity.ai.DelayedMeleeAttackGoal; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.ai.goal.RandomStrollGoal; +import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; +import net.minecraft.world.entity.ai.navigation.PathNavigation; +import net.minecraft.world.entity.monster.Monster; +import net.minecraft.world.entity.npc.AbstractVillager; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; + +public class MarauderEntity extends Monster { + + /** + * Handles the animation state transitions for the {@link MarauderEntity}. This dispatcher is responsible for + * deciding and applying the appropriate animations to the entity based on its current state and actions, such as + * walking, running, idling, spawning, attacking, or dying. This instance operates primarily on the client side to + * handle visual representation of the {@link MarauderEntity} and is updated within the entity's tick lifecycle. + */ + public final MarauderAnimationDispatcher animationDispatcher; + + private final MoveAnalysis moveAnalysis; + + public MarauderEntity(EntityType entityType, Level level) { + super(entityType, level); + this.animationDispatcher = new MarauderAnimationDispatcher(this); + this.moveAnalysis = new MoveAnalysis(this); + } + + @Override + protected @NotNull PathNavigation createNavigation(@NotNull Level level) { + return new AzureNavigation(this, level); + } + + @Override + public float maxUpStep() { + return 2.0F; + } + + @Override + protected void tickDeath() { + ++this.deathTime; + if (this.deathTime >= 80 && !this.level().isClientSide() && !this.isRemoved()) { + this.level().broadcastEntityEvent(this, (byte) 60); + this.remove(RemovalReason.KILLED); + } + } + + @Override + public void tick() { + super.tick(); + moveAnalysis.update(); + + if (this.level().isClientSide) { + var isMovingOnGround = moveAnalysis.isMovingHorizontally() && onGround(); + Runnable animationRunner; + if (!this.isAlive()) { + animationRunner = animationDispatcher::clientDeath; + // } else if (this.tickCount < 270) { + // animationDispatcher.clientSpawn(); + } else if (isMovingOnGround) { + if (this.isAggressive()) { + animationRunner = animationDispatcher::clientRun; + } else { + animationRunner = animationDispatcher::clientWalk; + } + } else { + animationRunner = animationDispatcher::clientIdle; + } + animationRunner.run(); + } else { + // if (this.tickCount < 280 && this.isAlive()) { + // if (this.getNavigation() instanceof AzureNavigation azureNavigation) { + // azureNavigation.hardStop(); + // azureNavigation.stop(); + // } + // this.setYBodyRot(0); + // this.setYHeadRot(0); + // this.getEyePosition(90); + // this.setXRot(0); + // this.setYRot(0); + // } + } + } + + /** + * TODO: Get longer Melee animations working + */ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(7, new RandomStrollGoal(this, 0.3F)); + this.goalSelector.addGoal(2, new DelayedMeleeAttackGoal(this, 0.6F, true)); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true)); + } + + @Override + protected void playStepSound(@NotNull BlockPos pos, @NotNull BlockState state) {} +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java new file mode 100644 index 000000000..64322520b --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java @@ -0,0 +1,26 @@ +package mod.azure.azurelib.testing.entity; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.render.entity.AzEntityRenderer; +import mod.azure.azurelib.rewrite.render.entity.AzEntityRendererConfig; +import mod.azure.azurelib.rewrite.render.layer.AzAutoGlowingLayer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; + +public class MarauderRenderer extends AzEntityRenderer { + + private static final ResourceLocation MODEL = AzureLib.modResource("geo/entity/marauder.geo.json"); + + private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/entity/marauder.png"); + + public MarauderRenderer(EntityRendererProvider.Context context) { + super( + AzEntityRendererConfig.builder(MODEL, TEXTURE) + .addRenderLayer(new AzAutoGlowingLayer<>()) + .setAnimatorProvider(MarauderAnimator::new) + .setDeathMaxRotation(0F) + .build(), + context + ); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/ai/DelayedMeleeAttackGoal.java b/common/src/main/java/mod/azure/azurelib/testing/entity/ai/DelayedMeleeAttackGoal.java new file mode 100644 index 000000000..638adfef9 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/entity/ai/DelayedMeleeAttackGoal.java @@ -0,0 +1,179 @@ +package mod.azure.azurelib.testing.entity.ai; + +import mod.azure.azurelib.testing.entity.MarauderEntity; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.EntitySelector; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.pathfinder.Path; + +import java.util.EnumSet; + +public class DelayedMeleeAttackGoal extends Goal { + + protected final PathfinderMob mob; + + private final double speedModifier; + + private final boolean followingTargetEvenIfNotSeen; + + public Path path; + + private double pathedTargetX; + + private double pathedTargetY; + + private double pathedTargetZ; + + private int ticksUntilNextPathRecalculation; + + private int ticksUntilNextAttack; + + public long lastCanUseCheck; + + private static final long COOLDOWN_BETWEEN_CAN_USE_CHECKS = 20L; + + private int delayCounter; + + public DelayedMeleeAttackGoal(PathfinderMob mob, double speedModifier, boolean followingTargetEvenIfNotSeen) { + this.mob = mob; + this.speedModifier = speedModifier; + this.followingTargetEvenIfNotSeen = followingTargetEvenIfNotSeen; + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + } + + public boolean canUse() { + long i = this.mob.level().getGameTime(); + if (i - this.lastCanUseCheck < 20L) { + return false; + } else { + this.lastCanUseCheck = i; + LivingEntity livingentity = this.mob.getTarget(); + if (livingentity == null) { + return false; + } else if (!livingentity.isAlive()) { + return false; + } else { + this.path = this.mob.getNavigation().createPath(livingentity, 0); + return this.path != null ? true : this.mob.isWithinMeleeAttackRange(livingentity); + } + } + } + + public boolean canContinueToUse() { + LivingEntity livingentity = this.mob.getTarget(); + if (livingentity == null) { + return false; + } else if (!livingentity.isAlive()) { + return false; + } else if (!this.followingTargetEvenIfNotSeen) { + return !this.mob.getNavigation().isDone(); + } else { + return !this.mob.isWithinRestriction(livingentity.blockPosition()) + ? false + : !(livingentity instanceof Player) || !livingentity.isSpectator() && !((Player) livingentity) + .isCreative(); + } + } + + public void start() { + this.mob.getNavigation().moveTo(this.path, this.speedModifier); + this.mob.setAggressive(true); + this.ticksUntilNextPathRecalculation = 0; + this.ticksUntilNextAttack = 0; + this.delayCounter = 0; + } + + public void stop() { + LivingEntity livingentity = this.mob.getTarget(); + if (!EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(livingentity)) { + this.mob.setTarget((LivingEntity) null); + } + + this.mob.setAggressive(false); + this.mob.getNavigation().stop(); + this.delayCounter = 0; + } + + public boolean requiresUpdateEveryTick() { + return true; + } + + public void tick() { + LivingEntity livingentity = this.mob.getTarget(); + if (livingentity != null) { + if (!this.mob.level().isClientSide()) { + this.delayCounter++; + } + this.mob.getLookControl().setLookAt(livingentity, 30.0F, 30.0F); + this.ticksUntilNextPathRecalculation = Math.max(this.ticksUntilNextPathRecalculation - 1, 0); + if ( + (this.followingTargetEvenIfNotSeen || this.mob.getSensing().hasLineOfSight(livingentity)) + && this.ticksUntilNextPathRecalculation <= 0 && (this.pathedTargetX == (double) 0.0F + && this.pathedTargetY == (double) 0.0F && this.pathedTargetZ == (double) 0.0F || livingentity + .distanceToSqr(this.pathedTargetX, this.pathedTargetY, this.pathedTargetZ) >= (double) 1.0F + || this.mob.getRandom().nextFloat() < 0.05F) + ) { + this.pathedTargetX = livingentity.getX(); + this.pathedTargetY = livingentity.getY(); + this.pathedTargetZ = livingentity.getZ(); + this.ticksUntilNextPathRecalculation = 4 + this.mob.getRandom().nextInt(7); + double d0 = this.mob.distanceToSqr(livingentity); + if (d0 > (double) 1024.0F) { + this.ticksUntilNextPathRecalculation += 10; + } else if (d0 > (double) 256.0F) { + this.ticksUntilNextPathRecalculation += 5; + } + + if (!this.mob.getNavigation().moveTo(livingentity, this.speedModifier)) { + this.ticksUntilNextPathRecalculation += 15; + } + + this.ticksUntilNextPathRecalculation = this.adjustedTickDelay(this.ticksUntilNextPathRecalculation); + } + + if ( + !this.mob.level().isClientSide() && this.mob.getTarget() != null && this.mob.isWithinMeleeAttackRange( + this.mob.getTarget() + ) + ) { + ((MarauderEntity) this.mob).animationDispatcher.serverMelee(); + } + this.ticksUntilNextAttack = Math.max(this.ticksUntilNextAttack - 1, 0); + this.checkAndPerformAttack(livingentity); + } + } + + protected void checkAndPerformAttack(LivingEntity target) { + if (this.canPerformAttack(target)) { + this.resetAttackCooldown(); + this.mob.swing(InteractionHand.MAIN_HAND); + this.mob.doHurtTarget(target); + } + } + + protected void resetAttackCooldown() { + this.ticksUntilNextAttack = this.adjustedTickDelay(80); + this.delayCounter = 0; + } + + protected boolean isTimeToAttack() { + return this.ticksUntilNextAttack <= 0; + } + + protected boolean canPerformAttack(LivingEntity entity) { + return this.delayCounter > 60 && this.isTimeToAttack() && this.mob.isWithinMeleeAttackRange(entity) && this.mob + .getSensing() + .hasLineOfSight(entity); + } + + protected int getTicksUntilNextAttack() { + return this.ticksUntilNextAttack; + } + + protected int getAttackInterval() { + return this.adjustedTickDelay(80); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimationDispatcher.java b/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimationDispatcher.java new file mode 100644 index 000000000..0e64a0227 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimationDispatcher.java @@ -0,0 +1,20 @@ +package mod.azure.azurelib.testing.item; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; +import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviors; +import mod.azure.azurelib.testing.CommonStrings; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; + +public class PistolAnimationDispatcher { + + private static final AzCommand FIRING_COMMAND = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.FIRING_ANIMATION_NAME, + AzPlayBehaviors.PLAY_ONCE + ); + + public void serverFire(Entity entity, ItemStack itemStack) { + FIRING_COMMAND.sendForItem(entity, itemStack); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimator.java b/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimator.java new file mode 100644 index 000000000..2cf491539 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimator.java @@ -0,0 +1,34 @@ +package mod.azure.azurelib.testing.item; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; +import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; +import mod.azure.azurelib.testing.CommonStrings; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class PistolAnimator extends AzItemAnimator { + + private static final ResourceLocation ANIMATIONS = AzureLib.modResource("animations/item/pistol.animation.json"); + + public PistolAnimator() { + super(AzAnimatorConfig.defaultConfig()); + } + + @Override + public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { + animationControllerContainer.add( + AzAnimationController.builder(this, CommonStrings.BASE_CONTROLLER) + .build() + ); + } + + @Override + public @NotNull ResourceLocation getAnimationLocation(ItemStack animatable) { + return ANIMATIONS; + } + +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java b/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java new file mode 100644 index 000000000..e2687e6d4 --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java @@ -0,0 +1,45 @@ +package mod.azure.azurelib.testing.item; + +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +public class PistolItem extends Item { + + private final PistolAnimationDispatcher dispatcher; + + public PistolItem() { + super(new Properties()); + this.dispatcher = new PistolAnimationDispatcher(); + } + + @Override + public void onUseTick( + @NotNull Level level, + @NotNull LivingEntity livingEntity, + @NotNull ItemStack stack, + int remainingUseDuration + ) { + super.onUseTick(level, livingEntity, stack, remainingUseDuration); + if (livingEntity instanceof Player player && !level.isClientSide()) { + dispatcher.serverFire(player, stack); + } + } + + @Override + public @NotNull InteractionResultHolder use( + @NotNull Level world, + Player user, + @NotNull InteractionHand hand + ) { + final var itemStack = user.getItemInHand(hand); + user.startUsingItem(hand); + return InteractionResultHolder.consume(itemStack); + } +} diff --git a/common/src/main/java/mod/azure/azurelib/testing/item/PistolRenderer.java b/common/src/main/java/mod/azure/azurelib/testing/item/PistolRenderer.java new file mode 100644 index 000000000..26b907d7f --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/testing/item/PistolRenderer.java @@ -0,0 +1,21 @@ +package mod.azure.azurelib.testing.item; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.render.item.AzItemRenderer; +import mod.azure.azurelib.rewrite.render.item.AzItemRendererConfig; +import net.minecraft.resources.ResourceLocation; + +public class PistolRenderer extends AzItemRenderer { + + private static final ResourceLocation MODEL = AzureLib.modResource("geo/item/pistol.geo.json"); + + private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/item/pistol.png"); + + public PistolRenderer() { + super( + AzItemRendererConfig.builder(itemStack -> MODEL, itemStack -> TEXTURE) + .setAnimatorProvider(PistolAnimator::new) + .build() + ); + } +} diff --git a/common/src/main/resources/assets/azurelib/animations/block/stargate.animation.json b/common/src/main/resources/assets/azurelib/animations/block/stargate.animation.json new file mode 100644 index 000000000..fac7d0e36 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/animations/block/stargate.animation.json @@ -0,0 +1,249 @@ +{ + "format_version": "1.8.0", + "animations": { + "spinning": { + "loop": true, + "animation_length": 14.4, + "bones": { + "bone2": { + "position": { + "1.0": { + "vector": [0, 0, 0] + }, + "1.08": { + "vector": [0, "math.sin(query.anim_time * 360) * -3", 0] + }, + "1.12": { + "vector": [0, 0, 0] + }, + "1.56": { + "vector": [0, 0, 0] + }, + "2.64": { + "vector": [0, 0, 0] + }, + "2.72": { + "vector": [0, "math.cos(query.anim_time * 360) * -3", 0] + }, + "2.76": { + "vector": [0, 0, 0] + }, + "3.2": { + "vector": [0, 0, 0] + }, + "4.32": { + "vector": [0, 0, 0] + }, + "4.4": { + "vector": [0, "math.sin(query.anim_time * 360) * -2", 0] + }, + "4.44": { + "vector": [0, 0, 0] + }, + "4.88": { + "vector": [0, 0, 0] + }, + "6.0": { + "vector": [0, 0, 0] + }, + "6.08": { + "vector": [0, "math.sin(query.anim_time * 360) * -3", 0] + }, + "6.12": { + "vector": [0, 0, 0] + }, + "6.56": { + "vector": [0, 0, 0] + }, + "7.68": { + "vector": [0, 0, 0] + }, + "7.76": { + "vector": [0, "math.cos(query.anim_time * 360) * -3", 0] + }, + "7.8": { + "vector": [0, 0, 0] + }, + "8.24": { + "vector": [0, 0, 0] + }, + "9.36": { + "vector": [0, 0, 0] + }, + "9.44": { + "vector": [0, "math.sin(query.anim_time * 360) * -3", 0] + }, + "9.48": { + "vector": [0, 0, 0] + }, + "9.92": { + "vector": [0, 0, 0] + }, + "10.96": { + "vector": [0, 0, 0] + }, + "11.04": { + "vector": [0, "math.sin(query.anim_time * 360) * -3", 0] + }, + "11.08": { + "vector": [0, 0, 0] + }, + "11.52": { + "vector": [0, 0, 0] + } + } + }, + "inner_ring": { + "rotation": { + "0.0": { + "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] + }, + "1.04": { + "vector": [0, 0, 360] + }, + "1.48": { + "vector": [0, 0, 359] + }, + "1.52": { + "vector": [0, 0, "math.cos(query.anim_time * 360) * -360"] + }, + "2.56": { + "vector": [0, 0, 1] + }, + "3.16": { + "vector": [0, 0, 1] + }, + "3.2": { + "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] + }, + "4.32": { + "vector": [0, 0, 1] + }, + "5.24": { + "vector": [0, 0, 1] + }, + "5.28": { + "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] + }, + "5.88": { + "vector": [0, 0, 1] + }, + "5.92": { + "vector": [0, 0, 0] + }, + "6.92": { + "vector": [0, 0, 0] + }, + "6.96": { + "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] + }, + "7.56": { + "vector": [0, 0, 1] + }, + "7.6": { + "vector": [0, 0, 0] + }, + "8.12": { + "vector": [0, 0, 0] + }, + "8.16": { + "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] + }, + "9.2": { + "vector": [0, 0, 1] + }, + "9.28": { + "vector": [0, 0, 0] + }, + "9.84": { + "vector": [0, 0, 0] + }, + "10.88": { + "vector": [0, 0, 359] + }, + "10.96": { + "vector": [0, 0, 360] + } + } + }, + "portal": { + "scale": { + "0.0": { + "vector": [0, 0, 0] + }, + "11.48": { + "vector": [0, 0, 0] + }, + "11.52": { + "vector": [1, 1, 1] + }, + "12.4": { + "vector": [1, 1, 1] + } + } + }, + "portal2": { + "rotation": { + "11.52": { + "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] + }, + "13.44": { + "vector": [0, 0, 359] + } + }, + "position": { + "11.52": { + "vector": [0, 0, 0] + }, + "12.4": { + "vector": [0, 0, "-30.83+math.sin(query.anim_time * 200) * 10"] + }, + "13.44": { + "vector": [0, 0, 0] + } + }, + "scale": { + "0.0": { + "vector": [0, 0, 0] + }, + "11.52": { + "vector": [0, 0, 0] + }, + "12.4": { + "vector": [0.5, 0.5, 37.3] + }, + "13.44": { + "vector": [0, 0, 0] + } + } + } + } + }, + "idle": { + "loop": true, + "bones": { + "portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "portal2": { + "scale": { + "vector": [0, 0, 0] + } + } + } + }, + "open": { + "loop": true, + "bones": { + "bone9": { + "scale": { + "vector": [1, 1, 1] + } + } + } + } + }, + "azurelib_format_version": 2 +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/animations/entity/marauder.animation.json b/common/src/main/resources/assets/azurelib/animations/entity/marauder.animation.json new file mode 100644 index 000000000..94ee538c4 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/animations/entity/marauder.animation.json @@ -0,0 +1,6262 @@ +{ + "format_version": "1.8.0", + "animations": { + "idle": { + "loop": true, + "animation_length": 2, + "bones": { + "torso": { + "rotation": { + "vector": [0, 22.5, 0] + } + }, + "right_arm": { + "rotation": { + "vector": [36.94319, 29.52361, 9.71415] + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0.5, 0] + }, + "2.0": { + "vector": [0, 0, 0] + } + } + }, + "left_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.2": { + "vector": [-2.83, 0, 0] + }, + "0.8": { + "vector": [-7.17, 0, 0] + }, + "1.0": { + "vector": [-7.5, 0, 0] + }, + "1.2": { + "vector": [-7.17, 0, 0] + }, + "1.8": { + "vector": [-2.83, 0, 0] + }, + "2.0": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, -1.5] + }, + "0.2": { + "vector": [0, -0.25, -1.5] + }, + "0.8": { + "vector": [0, -0.25, -1.5] + }, + "1.0": { + "vector": [0, -0.25, -1.5] + }, + "1.2": { + "vector": [0, -0.25, -1.5] + }, + "1.8": { + "vector": [0, -0.25, -1.5] + }, + "2.0": { + "vector": [0, -0.25, -1.5] + } + } + }, + "left_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "0.2": { + "vector": [5.67, 0, 0] + }, + "0.8": { + "vector": [14.33, 0, 0] + }, + "1.0": { + "vector": [15, 0, 0] + }, + "1.2": { + "vector": [14.33, 0, 0] + }, + "1.8": { + "vector": [5.67, 0, 0] + }, + "2.0": { + "vector": [5, 0, 0] + } + } + }, + "right_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.2": { + "vector": [-2.83, 0, 0] + }, + "0.8": { + "vector": [-7.17, 0, 0] + }, + "1.0": { + "vector": [-7.5, 0, 0] + }, + "1.2": { + "vector": [-7.17, 0, 0] + }, + "1.8": { + "vector": [-2.83, 0, 0] + }, + "2.0": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, 0.5] + }, + "0.2": { + "vector": [0, -0.25, 0.5] + }, + "0.8": { + "vector": [0, -0.25, 0.5] + }, + "1.0": { + "vector": [0, -0.25, 0.5] + }, + "1.2": { + "vector": [0, -0.25, 0.5] + }, + "1.8": { + "vector": [0, -0.25, 0.5] + }, + "2.0": { + "vector": [0, -0.25, 0.5] + } + } + }, + "right_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "0.2": { + "vector": [5.67, 0, 0] + }, + "0.8": { + "vector": [14.33, 0, 0] + }, + "1.0": { + "vector": [15, 0, 0] + }, + "1.2": { + "vector": [14.33, 0, 0] + }, + "1.8": { + "vector": [5.67, 0, 0] + }, + "2.0": { + "vector": [5, 0, 0] + } + } + }, + "h_head_furious": { + "rotation": { + "vector": [0, -22.5, 0] + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0.25, 0] + }, + "2.0": { + "vector": [0, 0, 0] + } + } + }, + "h_slash": { + "scale": { + "vector": [0, 0, 0] + } + }, + "inactive_axe": { + "scale": { + "vector": [0, 0, 0] + } + }, + "leg_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "body2": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, -0.5, 0] + }, + "2.0": { + "vector": [0, 0, 0] + } + } + }, + "dreadnought_portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_attack": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_normal": { + "scale": { + "vector": [1, 1, 1] + } + } + } + }, + "walk": { + "loop": true, + "animation_length": 2, + "bones": { + "torso": { + "rotation": { + "vector": [0, 22.5, 0] + }, + "position": { + "0.0": { + "vector": [-0.25, 0, 0] + }, + "0.3": { + "vector": [-0.33, 0, 0] + }, + "1.0": { + "vector": [0.25, 0, 0] + }, + "1.3": { + "vector": [0.33, 0, 0] + }, + "2.0": { + "vector": [-0.25, 0, 0] + } + } + }, + "upper_torso": { + "rotation": { + "0.0": { + "vector": [0, 0, 1] + }, + "0.3": { + "vector": [0, 0, 1.5] + }, + "1.0": { + "vector": [0, 0, -1] + }, + "1.3": { + "vector": [0, 0, -1.5] + }, + "2.0": { + "vector": [0, 0, 1] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [0, -0.17, 0] + }, + "0.2": { + "vector": [0, -0.83, 0] + }, + "0.3": { + "vector": [0, -1, 0] + }, + "0.45": { + "vector": [0, -0.93, 0] + }, + "0.85": { + "vector": [0, -0.07, 0] + }, + "1.0": { + "vector": [0, 0, 0] + }, + "1.1": { + "vector": [0, -0.17, 0] + }, + "1.2": { + "vector": [0, -0.83, 0] + }, + "1.3": { + "vector": [0, -1, 0] + }, + "1.45": { + "vector": [0, -0.93, 0] + }, + "1.85": { + "vector": [0, -0.07, 0] + }, + "2.0": { + "vector": [0, 0, 0] + } + } + }, + "right_arm": { + "rotation": { + "0.0": { + "vector": [36.94319, 29.52361, 9.71415] + }, + "0.3": { + "vector": [37.94, 29.52, 9.71] + }, + "1.0": { + "vector": [37.1047, 28.17926, 12.12078] + }, + "1.3": { + "vector": [36.1047, 28.17926, 12.12078] + }, + "2.0": { + "vector": [36.94319, 29.52361, 9.71415] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [0, 0.17, 0] + }, + "0.2": { + "vector": [0, 0.83, 0] + }, + "0.3": { + "vector": [0, 1, 0] + }, + "0.45": { + "vector": [0, 0.93, 0] + }, + "0.85": { + "vector": [0, 0.07, 0] + }, + "1.0": { + "vector": [0, 0, 0] + }, + "1.1": { + "vector": [0, 0.17, 0] + }, + "1.2": { + "vector": [0, 0.83, 0] + }, + "1.3": { + "vector": [0, 1, 0] + }, + "1.45": { + "vector": [0, 0.93, 0] + }, + "1.85": { + "vector": [0, 0.07, 0] + }, + "2.0": { + "vector": [0, 0, 0] + } + } + }, + "left_leg": { + "rotation": { + "0.0": { + "vector": [12.5, 0, 0] + }, + "0.55": { + "vector": [-10, 0, 0] + }, + "1.0": { + "vector": [-9.96271, 0.86717, 4.92442] + }, + "2.0": { + "vector": [12.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, -0.5, -1] + }, + "0.15": { + "vector": [0, 1, -2] + }, + "0.55": { + "vector": [0, 1, -2] + }, + "1.0": { + "vector": [0, -1, -2.5] + }, + "2.0": { + "vector": [0, -0.5, -1] + } + } + }, + "left_knee": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [32.5, 0, 0] + }, + "1.0": { + "vector": [5, 0, 0] + }, + "2.0": { + "vector": [0, 0, 0] + } + } + }, + "right_leg": { + "rotation": { + "0.0": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "1.0": { + "vector": [12.5, 0, 0] + }, + "1.55": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "2.0": { + "vector": [-9.96271, -0.86717, -4.92442] + } + }, + "position": { + "0.0": { + "vector": [0, -1, -0.5] + }, + "1.0": { + "vector": [0, -0.5, 1] + }, + "1.15": { + "vector": [0, 1, 0] + }, + "1.55": { + "vector": [0, 1, 0] + }, + "2.0": { + "vector": [0, -1, -0.5] + } + } + }, + "right_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "1.0": { + "vector": [0, 0, 0] + }, + "1.3": { + "vector": [32.5, 0, 0] + }, + "2.0": { + "vector": [5, 0, 0] + } + } + }, + "h_head_furious": { + "rotation": { + "0.0": { + "vector": [0, -22.5, -1] + }, + "0.3": { + "vector": [0, -22.5, -1.5] + }, + "1.0": { + "vector": [0, -22.5, 1] + }, + "1.3": { + "vector": [0, -22.5, 1.5] + }, + "2.0": { + "vector": [0, -22.5, -1] + } + } + }, + "inactive_axe": { + "scale": { + "vector": [0, 0, 0] + } + }, + "leg_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "h_slash": { + "scale": { + "vector": [0, 0, 0] + } + }, + "dreadnought_portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_attack": { + "scale": { + "vector": [0, 0, 0] + } + } + }, + "sound_effects": { + "0.95": { + "effect": "walk" + }, + "1.95": { + "effect": "walk" + } + } + }, + "run": { + "loop": true, + "animation_length": 0.5, + "bones": { + "torso": { + "rotation": { + "vector": [25, 0, 0] + } + }, + "left_arm": { + "rotation": { + "0.0": { + "vector": [-47.41709, -12.48859, 0.54146] + }, + "0.1": { + "vector": [-16.79911, -16.90142, 4.98561] + }, + "0.25": { + "vector": [39.3969, -19.7052, 3.8848] + }, + "0.5": { + "vector": [-47.41709, -12.48859, 0.54146] + } + } + }, + "left_elbow": { + "rotation": { + "0.0": { + "vector": [-27.5, 0, 0] + }, + "0.5": { + "vector": [-27.5, 0, 0] + } + } + }, + "right_arm": { + "rotation": { + "0.0": { + "vector": [39.3969, 19.7052, -3.8848] + }, + "0.25": { + "vector": [-47.41709, 12.4886, -0.5415] + }, + "0.35": { + "vector": [-16.79911, 16.9014, -4.9856] + }, + "0.5": { + "vector": [39.3969, 19.7052, -3.8848] + } + } + }, + "right_elbow": { + "rotation": { + "0.0": { + "vector": [-27.5, 0, 0] + }, + "0.5": { + "vector": [-27.5, 0, 0] + } + } + }, + "axe": { + "rotation": { + "vector": [-45, -45, 0] + } + }, + "left_leg": { + "rotation": { + "0.0": { + "vector": [48.84318, 11.0235, -2.4076] + }, + "0.25": { + "vector": [-47.31623, 14.328, 1.6962] + }, + "0.35": { + "vector": [-16.25675, 7.3218, 2.8407] + }, + "0.5": { + "vector": [48.84318, 11.0235, -2.4076] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, -1, -2] + }, + "0.5": { + "vector": [0, 0, 0] + } + } + }, + "left_knee": { + "rotation": { + "0.0": { + "vector": [41.96457, 0, 0] + }, + "0.1": { + "vector": [22.01779, 0, 0] + }, + "0.25": { + "vector": [10.44841, 0, 0] + }, + "0.5": { + "vector": [41.96457, 0, 0] + } + } + }, + "right_leg": { + "rotation": { + "0.0": { + "vector": [-47.31623, -14.32798, -1.69619] + }, + "0.1": { + "vector": [-16.25675, -7.32179, -2.84073] + }, + "0.25": { + "vector": [48.84318, -11.02346, 2.40756] + }, + "0.5": { + "vector": [-47.31623, -14.32798, -1.69619] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [0, -1, -2] + }, + "0.25": { + "vector": [0, 0, 0] + }, + "0.5": { + "vector": [0, 0, 0] + } + } + }, + "right_knee": { + "rotation": { + "0.0": { + "vector": [10.44841, 0, 0] + }, + "0.25": { + "vector": [41.96457, 0, 0] + }, + "0.35": { + "vector": [22.01779, 0, 0] + }, + "0.5": { + "vector": [10.44841, 0, 0] + } + } + }, + "h_head_furious": { + "rotation": { + "vector": [-15, 0, 0] + } + }, + "inactive_axe": { + "scale": { + "vector": [0, 0, 0] + } + }, + "leg_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "h_slash": { + "scale": { + "vector": [0, 0, 0] + } + }, + "body2": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [0, -2, 0] + }, + "0.2": { + "vector": [0, 1.5, 0] + }, + "0.25": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, -2, 0] + }, + "0.45": { + "vector": [0, 1.5, 0] + }, + "0.5": { + "vector": [0, 0, 0] + } + } + }, + "dreadnought_portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_attack": { + "scale": { + "vector": [0, 0, 0] + } + } + }, + "sound_effects": { + "0.05": { + "effect": "walk" + }, + "0.15": { + "effect": "walk" + }, + "0.3": { + "effect": "walk" + }, + "0.4": { + "effect": "walk" + } + } + }, + "energy_slash": { + "animation_length": 1, + "bones": { + "torso": { + "rotation": { + "0.0": { + "vector": [0, 22.5, 0] + }, + "0.25": { + "vector": [-22.45376, 4.88119, 1.08482], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [20.15711, -30.42471, 3.15289], + "easing": "easeInOutQuad" + }, + "1.0": { + "vector": [0, 22.5, 0], + "easing": "easeInOutQuad" + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.2": { + "vector": [0, 0.05, 0] + }, + "0.25": { + "vector": [0, 0.8, 0] + }, + "0.4": { + "vector": [0, 0.95, 0] + }, + "0.5": { + "vector": [0, 0.95, 0] + }, + "0.65": { + "vector": [0, 0.8, 0] + }, + "1.0": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + } + } + }, + "left_arm": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-42.73421, -15.69986, -38.82495], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [-25.00772, 13.4395, -26.99582], + "easing": "easeInOutQuad" + }, + "1.0": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + } + } + }, + "left_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-45, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [-22.5, 0, 0], + "easing": "easeInOutQuad" + }, + "1.0": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + } + } + }, + "right_arm": { + "rotation": { + "0.0": { + "vector": [36.94319, 29.52361, 9.71415] + }, + "0.25": { + "vector": [-170.91589, -12.68945, -29.87236], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [-50.77424, 27.00637, -42.94328], + "easing": "easeInOutQuad" + }, + "0.5": { + "vector": [-21.42177, 17.7925, -31.36258], + "easing": "easeInOutQuad" + }, + "1.0": { + "vector": [36.94319, 29.52361, 9.71415], + "easing": "easeInOutQuad" + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + } + } + }, + "axe": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-45, -45, 15], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [22.5, -45, 15], + "easing": "easeInOutQuad" + }, + "1.0": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + } + } + }, + "left_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.25": { + "vector": [-56.86218, 2.34352, -12.28179] + }, + "0.4": { + "vector": [-32.5, 0, 0] + }, + "0.5": { + "vector": [-32.5, 0, 0] + }, + "1.0": { + "vector": [-2.5, 0, 0], + "easing": "easeInOutQuad" + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, -1.5] + }, + "1.0": { + "vector": [0, -0.25, -1.5], + "easing": "easeInOutQuad" + } + } + }, + "left_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "0.25": { + "vector": [45, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [45, 0, 0], + "easing": "easeInOutQuad" + }, + "0.5": { + "vector": [45, 0, 0], + "easing": "easeInOutQuad" + }, + "1.0": { + "vector": [5, 0, 0], + "easing": "easeInOutQuad" + } + } + }, + "right_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.25": { + "vector": [10.4943, -0.822, -2.71827] + }, + "0.4": { + "vector": [37.5, 0, 0] + }, + "0.5": { + "vector": [37.5, 0, 0] + }, + "1.0": { + "vector": [-2.5, 0, 0], + "easing": "easeInOutQuad" + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, 0.5] + }, + "1.0": { + "vector": [0, -0.25, 0.5], + "easing": "easeInOutQuad" + } + } + }, + "right_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "1.0": { + "vector": [5, 0, 0], + "easing": "easeInOutQuad" + } + } + }, + "h_slash": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 4, -8] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + }, + "scale": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.35": { + "vector": [1, 1, 1], + "easing": "easeInOutQuad" + } + } + }, + "h_slash2": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + } + }, + "h_slash3": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + } + }, + "h_slash4": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + } + }, + "h_slash5": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + } + }, + "h_slash6": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + } + }, + "h_slash7": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + } + }, + "h_slash8": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + } + }, + "h_slash9": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + } + }, + "h_slash10": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + }, + "0.4": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, -47], + "easing": "easeInOutQuad" + } + } + }, + "h_head_furious": { + "rotation": { + "0.0": { + "vector": [0, -22.5, 0] + }, + "0.25": { + "vector": [33.77048, -5.99032, 2.66499] + }, + "0.4": { + "vector": [-5.12161, 25.82329, -2.83601] + }, + "0.5": { + "vector": [-5.12161, 25.82329, -2.83601] + }, + "0.75": { + "vector": [-0.75543, 4.97912, -2.45465] + }, + "1.0": { + "vector": [0, -22.5, 0], + "easing": "easeInOutQuad" + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + } + } + }, + "inactive_axe": { + "scale": { + "vector": [0, 0, 0] + } + }, + "leg_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "body2": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.15": { + "vector": [0, -1, 2] + }, + "0.3": { + "vector": [0, -1, 2] + }, + "0.4": { + "vector": [0, -4, -8] + }, + "1.0": { + "vector": [0, 0, 0], + "easing": "easeInOutQuad" + } + } + }, + "dreadnought_portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_normal": { + "scale": { + "vector": [0, 0, 0] + } + } + }, + "sound_effects": { + "0.35": { + "effect": "slash" + } + } + }, + "hook": { + "animation_length": 1, + "bones": { + "torso": { + "rotation": { + "0.0": { + "vector": [0, 22.5, 0] + }, + "0.35": { + "vector": [0, -45, 0] + }, + "0.5": { + "vector": [30, 45, 0] + }, + "0.6": { + "vector": [63.10819, 71.24264, 36.51548] + }, + "1.0": { + "vector": [0, 22.5, 0] + } + } + }, + "left_arm": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.4": { + "vector": [62.65725, 6.55676, -77.51703] + }, + "0.5": { + "vector": [-67.34275, 6.55676, -77.51703] + }, + "0.6": { + "vector": [-104.84275, 6.55676, -77.51703] + }, + "1.0": { + "vector": [0, 0, 0] + } + } + }, + "left_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-60, 0, 0] + }, + "0.4": { + "vector": [-91.5, 0, 0] + }, + "0.5": { + "vector": [-37.5, 0, 0] + }, + "0.6": { + "vector": [-30.5, 0, 0] + }, + "1.0": { + "vector": [0, 0, 0] + } + } + }, + "right_arm": { + "rotation": { + "0.0": { + "vector": [36.94319, 29.52361, 9.71415] + }, + "0.35": { + "vector": [42.52426, 19.88275, 12.4118] + }, + "0.4": { + "vector": [42.52426, 19.88275, 12.4118] + }, + "0.45": { + "vector": [10.82416, 8.65125, 9.70546] + }, + "0.5": { + "vector": [-5.0275, -2.11363, 18.00718] + }, + "0.6": { + "vector": [-14.10919, -0.72303, 17.14132] + }, + "1.0": { + "vector": [36.94319, 29.52361, 9.71415] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [1, 0, 1] + }, + "0.5": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, 0] + } + } + }, + "right_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [22.5, 0, 0] + }, + "0.35": { + "vector": [22.5, 0, 0] + }, + "0.4": { + "vector": [0, 0, 0] + }, + "0.5": { + "vector": [0, 0, 0] + }, + "0.6": { + "vector": [7.5, 0, 0] + }, + "1.0": { + "vector": [0, 0, 0] + } + } + }, + "left_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.4": { + "vector": [-7.5, 0, 0] + }, + "0.5": { + "vector": [-2.5, 0, 0] + }, + "1.0": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, -1.5] + }, + "0.5": { + "vector": [0, -0.25, -1.5] + }, + "1.0": { + "vector": [0, -0.25, -1.5] + } + } + }, + "left_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "0.4": { + "vector": [12.5, 0, 0] + }, + "0.5": { + "vector": [5, 0, 0] + }, + "1.0": { + "vector": [5, 0, 0] + } + } + }, + "right_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.4": { + "vector": [-7.5, 0, 0] + }, + "0.5": { + "vector": [-2.5, 0, 0] + }, + "1.0": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, 0.5] + }, + "0.5": { + "vector": [0, -0.25, -1.5] + }, + "1.0": { + "vector": [0, -0.25, 0.5] + } + } + }, + "right_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "0.4": { + "vector": [12.5, 0, 0] + }, + "0.5": { + "vector": [5, 0, 0] + }, + "1.0": { + "vector": [5, 0, 0] + } + } + }, + "h_head_furious": { + "rotation": { + "0.0": { + "vector": [0, -22.5, 0] + }, + "0.25": { + "vector": [18.81517, 32.16683, 2.79164] + }, + "0.3": { + "vector": [17.23042, 35.45167, 3.72115] + }, + "0.35": { + "vector": [15.61275, 38.78143, 4.50441] + }, + "0.4": { + "vector": [1.82829, 15.09442, -2.58693] + }, + "0.5": { + "vector": [-3.83528, -41.13981, 7.84775] + }, + "0.6": { + "vector": [-20.07884, -63.15509, 26.26946] + }, + "0.8": { + "vector": [-0.62387, -43.76225, -0.56045] + }, + "1.0": { + "vector": [0, -22.5, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, 0] + } + } + }, + "inactive_axe": { + "scale": { + "vector": [0, 0, 0] + } + }, + "leg_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "h_slash": { + "scale": { + "vector": [0, 0, 0] + } + }, + "body2": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "1.0": { + "vector": [0, 0, 0] + } + } + }, + "dreadnought_portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_normal": { + "scale": { + "vector": [0, 0, 0] + } + } + }, + "sound_effects": { + "0.45": { + "effect": "axe_hit" + } + } + }, + "shoot": { + "animation_length": 1.25, + "bones": { + "torso": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [0, 3.75, 0] + }, + "0.2": { + "vector": [0, 18.75, 0] + }, + "0.3": { + "vector": [0, 22.5, 0] + }, + "0.55": { + "vector": [0, 22.5, 0] + }, + "0.75": { + "vector": [0, 22.5, 0] + }, + "0.85": { + "vector": [0, 20.89, 0] + }, + "1.15": { + "vector": [0, 1.61, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "upper_torso": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [0, 3.75, 0] + }, + "0.2": { + "vector": [0, 18.75, 0] + }, + "0.3": { + "vector": [0, 22.5, 0] + }, + "0.55": { + "vector": [0, 22.5, 0] + }, + "0.65": { + "vector": [-3.53092, 22.36846, -2.70099] + }, + "0.75": { + "vector": [-3.53, 22.37, -2.7] + }, + "0.85": { + "vector": [-3.28, 20.77, -2.51] + }, + "1.15": { + "vector": [-0.25, 1.6, -0.19] + }, + "1.25": { + "vector": [0, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.65": { + "vector": [0, 0, 1] + }, + "0.75": { + "vector": [0, 0, 0] + }, + "0.85": { + "vector": [0, 0, 0] + }, + "1.15": { + "vector": [0, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "left_arm": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [-10.25, -7.3, 2.45] + }, + "0.2": { + "vector": [-51.27, -36.49, 12.26] + }, + "0.3": { + "vector": [-61.52003, -43.79162, 14.71526] + }, + "0.55": { + "vector": [-61.52003, -43.79162, 14.71526] + }, + "0.65": { + "vector": [-106.52003, -43.79162, 14.71526] + }, + "0.75": { + "vector": [-106.52, -43.79, 14.72] + }, + "0.85": { + "vector": [-98.91, -40.66, 13.67] + }, + "1.15": { + "vector": [-7.61, -3.13, 1.05] + }, + "1.25": { + "vector": [0, 0, 0] + } + }, + "position": { + "0.55": { + "vector": [0, 0, 0] + }, + "0.65": { + "vector": [0, 0, 1] + }, + "0.75": { + "vector": [0, 0, 0] + }, + "0.85": { + "vector": [0, 0, 0] + } + } + }, + "left_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.65": { + "vector": [-22.5, 0, 0] + }, + "0.75": { + "vector": [-22.5, 0, 0] + }, + "0.85": { + "vector": [-20.89, 0, 0] + }, + "1.15": { + "vector": [-1.61, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "right_arm": { + "rotation": { + "0.0": { + "vector": [36.94319, 29.52361, 9.71415] + }, + "0.1": { + "vector": [32.45, 24.6, 8.1] + }, + "0.2": { + "vector": [14.49, 4.92, 1.62] + }, + "0.3": { + "vector": [10, 0, 0] + }, + "0.75": { + "vector": [10, 0, 0] + }, + "0.85": { + "vector": [11.68, 1.85, 0.61] + }, + "1.15": { + "vector": [35.26, 27.68, 9.11] + }, + "1.25": { + "vector": [36.94319, 29.52361, 9.71415] + } + } + }, + "h_head_furious": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [0.83, -7.5, 0] + }, + "0.2": { + "vector": [4.17, -37.5, 0] + }, + "0.3": { + "vector": [5, -45, 0] + }, + "0.4": { + "vector": [5, -45, 0] + }, + "0.75": { + "vector": [5, -45, 0] + }, + "0.85": { + "vector": [4.64, -41.79, 0] + }, + "1.15": { + "vector": [0.2, -1.77, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "inactive_axe": { + "scale": { + "vector": [0, 0, 0] + } + }, + "leg_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "h_slash": { + "scale": { + "vector": [0, 0, 0] + } + }, + "body2": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.65": { + "vector": [-2.5, 0, 0] + }, + "0.75": { + "vector": [0, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.65": { + "vector": [0, 0, 0.7] + }, + "0.75": { + "vector": [0, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "dreadnought_portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_normal": { + "scale": { + "vector": [0, 0, 0] + } + } + }, + "sound_effects": { + "0.45": { + "effect": "attack" + } + } + }, + "axe_attack": { + "animation_length": 1.25, + "override_previous_animation": true, + "bones": { + "torso": { + "rotation": { + "0.0": { + "vector": [0, 22.5, 0] + }, + "0.3": { + "vector": [0, 0, 0] + }, + "0.5": { + "vector": [0, 0, 0] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.6": { + "vector": [0, 19.42, 0] + }, + "0.8": { + "vector": [0, -15.24, 0] + }, + "1.25": { + "vector": [0, 22.5, 0] + } + } + }, + "upper_torso": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [0, 45, 0] + }, + "0.5": { + "vector": [0, 45, 0] + }, + "0.55": { + "vector": [0, -7.5, 0] + }, + "0.6": { + "vector": [0, 16.15, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "left_arm": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [-15.50853, 9.3733, -19.69937] + }, + "0.5": { + "vector": [-15.50853, 9.3733, -19.69937] + }, + "0.6": { + "vector": [20.14197, 18.45386, -44.18719] + }, + "0.9": { + "vector": [21.31475, -5.36143, -32.73013] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "left_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [-67.5, 0, 0] + }, + "0.5": { + "vector": [-67.5, 0, 0] + }, + "0.6": { + "vector": [-17.31, 0, 0] + }, + "0.9": { + "vector": [-17.31, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "right_arm": { + "rotation": { + "0.0": { + "vector": [36.94319, 29.52361, 9.71415] + }, + "0.3": { + "vector": [-109.70372, -34.12145, 43.97409] + }, + "0.5": { + "vector": [-121.22829, -24.29511, 67.25777] + }, + "0.55": { + "vector": [-102.45958, -14.42399, 77.57976] + }, + "0.6": { + "vector": [-29.07197, -13.47349, 60.63172] + }, + "0.7": { + "vector": [37.73643, -0.99934, 60.90401] + }, + "0.8": { + "vector": [52.73643, -0.99934, 60.90401] + }, + "1.25": { + "vector": [36.94319, 29.52361, 9.71415] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [2, -1, -1] + }, + "0.5": { + "vector": [2, -1, -1] + }, + "0.6": { + "vector": [2, 0, 0] + }, + "0.7": { + "vector": [2, 0, 0] + }, + "0.8": { + "vector": [2, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "right_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [0, 0, 0] + }, + "0.5": { + "vector": [0, 0, 0] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "axe": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [27.44634, -46.77638, 1.64566] + }, + "0.2": { + "vector": [12.8954, -72.37008, -0.60389] + }, + "0.3": { + "vector": [-1.09992, -78.07078, 2.27213] + }, + "0.5": { + "vector": [-19.66347, -69.27795, 32.98256] + }, + "0.55": { + "vector": [-26.9171, -41.80641, 26.33413] + }, + "0.6": { + "vector": [-26.9171, -41.80641, 26.33413] + }, + "0.7": { + "vector": [-4.4171, -41.80641, 26.33413] + }, + "0.75": { + "vector": [18.0829, -41.80641, 26.33413] + }, + "0.8": { + "vector": [40.32194, -42.2031, 21.34306] + }, + "1.25": { + "vector": [0, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.15": { + "vector": [0.8, -0.8, -2] + }, + "0.3": { + "vector": [0, 0, -1] + }, + "0.5": { + "vector": [0, 0, -1] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.6": { + "vector": [0, 0, 0] + }, + "0.7": { + "vector": [0, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "left_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.3": { + "vector": [-2.5, 0, 0] + }, + "0.5": { + "vector": [-2.5, 0, 0] + }, + "0.55": { + "vector": [-2.5, 0, 0] + }, + "0.6": { + "vector": [-18.7289, 36.8678, 1.46479] + }, + "0.65": { + "vector": [-39.75355, 33.51618, 1.33163] + }, + "1.25": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, -1.5] + }, + "0.3": { + "vector": [0, -0.25, -1.5] + }, + "0.5": { + "vector": [0, -0.25, -1.5] + }, + "0.55": { + "vector": [0, -0.25, -1.5] + }, + "1.25": { + "vector": [0, -0.25, -1.5] + } + } + }, + "left_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "0.3": { + "vector": [5, 0, 0] + }, + "0.45": { + "vector": [5, 0, 0] + }, + "0.55": { + "vector": [5, 0, 0] + }, + "0.6": { + "vector": [27.5, 0, 0] + }, + "0.65": { + "vector": [47.95, 0, 0] + }, + "1.25": { + "vector": [5, 0, 0] + } + } + }, + "right_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.3": { + "vector": [-2.5, 0, 0] + }, + "0.45": { + "vector": [-21.38885, 10.86362, 32.59688] + }, + "0.55": { + "vector": [-20.35186, 1.51634, 36.30248] + }, + "0.6": { + "vector": [27.22998, 57.15097, 37.99251] + }, + "1.25": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, 0.5] + }, + "0.3": { + "vector": [0, -0.25, 0.5] + }, + "0.45": { + "vector": [0, -0.25, 0.5] + }, + "0.55": { + "vector": [0, -0.25, 0.5] + }, + "1.25": { + "vector": [0, -0.25, 0.5] + } + } + }, + "right_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "0.3": { + "vector": [5, 0, 0] + }, + "0.45": { + "vector": [72.5, 0, 0] + }, + "0.55": { + "vector": [72.5, 0, 0] + }, + "0.6": { + "vector": [27.5, 0, 0] + }, + "1.25": { + "vector": [5, 0, 0] + } + } + }, + "h_head_furious": { + "rotation": { + "0.0": { + "vector": [0, -22.5, 0] + }, + "0.3": { + "vector": [0, 72.5, 0] + }, + "0.5": { + "vector": [0, 72.5, 0] + }, + "0.55": { + "vector": [0, 45, 0] + }, + "0.6": { + "vector": [0, 22.5, 0] + }, + "0.65": { + "vector": [0, 22.92, 0] + }, + "1.25": { + "vector": [0, -22.5, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [0, 0, 0] + }, + "0.5": { + "vector": [0, 0, 0] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.6": { + "vector": [0, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "inactive_axe": { + "scale": { + "vector": [0, 0, 0] + } + }, + "leg_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "h_slash": { + "scale": { + "vector": [0, 0, 0] + } + }, + "body2": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.15": { + "vector": [0, 75, 0] + }, + "0.3": { + "vector": [0, 100, 0] + }, + "0.5": { + "vector": [0, 220.04517, 0] + }, + "0.6": { + "vector": [0, 310.04517, 0] + }, + "0.65": { + "vector": [0, 310.04517, 0] + }, + "0.85": { + "vector": [0, 360, 0] + }, + "1.25": { + "vector": [0, 360, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [1, -1, 4] + }, + "0.4": { + "vector": [4.5, -1, 1] + }, + "0.45": { + "vector": [4.25, -1, -2.5] + }, + "0.5": { + "vector": [3, -1, -4] + }, + "0.55": { + "vector": [0, 0, -4] + }, + "0.65": { + "vector": [0, -4, -8] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "dreadnought_portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_normal": { + "scale": { + "vector": [0, 0, 0] + } + } + }, + "sound_effects": { + "0.25": { + "effect": "axe_hit" + } + } + }, + "axe_cut": { + "animation_length": 1.35, + "bones": { + "torso": { + "rotation": { + "0.0": { + "vector": [0, 22.5, 0] + }, + "0.3": { + "vector": [-12.5, 0, 0] + }, + "0.5": { + "vector": [0, 0, 0] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.6": { + "vector": [56.82632, 6.94314, -0.0723] + }, + "0.75": { + "vector": [56.82632, 6.94314, -0.0723] + }, + "1.25": { + "vector": [0, 22.5, 0] + } + } + }, + "upper_torso": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [0, 55, 0] + }, + "0.5": { + "vector": [0, 45, 0] + }, + "0.55": { + "vector": [0, 42, 0] + }, + "0.6": { + "vector": [0, -21, 0] + }, + "0.75": { + "vector": [0, -21, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + }, + "position": { + "vector": [0, 0, 0] + } + }, + "left_arm": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [0, 0, 0] + }, + "0.2": { + "vector": [-36.54219, -33.28982, -69.7671] + }, + "0.4": { + "vector": [-15.2553, -34.46461, -98.06686] + }, + "0.5": { + "vector": [-33.95742, -37.02514, -80.39611] + }, + "0.55": { + "vector": [-40.69539, -21.98867, -73.87325] + }, + "0.6": { + "vector": [-97.19705, 30.96904, -59.43905] + }, + "0.75": { + "vector": [-97.19705, 30.96904, -59.43905] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "left_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.3": { + "vector": [-45, 0, 0] + }, + "0.5": { + "vector": [-45, 0, 0] + }, + "0.6": { + "vector": [-34.81, 0, 0] + }, + "0.75": { + "vector": [-34.81, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "right_arm": { + "rotation": { + "0.0": { + "vector": [36.94319, 29.52361, 9.71415] + }, + "0.2": { + "vector": [-145.20556, -13.66588, 31.66413] + }, + "0.35": { + "vector": [-145.20556, -13.66588, 31.66413] + }, + "0.5": { + "vector": [-143.03203, -20.66768, 33.50612] + }, + "0.55": { + "vector": [-134.1591, -38.7886, 10.07665] + }, + "0.6": { + "vector": [-84.25071, -6.85667, -9.50735] + }, + "0.75": { + "vector": [-84.25071, -6.85667, -9.50735] + }, + "1.25": { + "vector": [36.94319, 29.52361, 9.71415] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.2": { + "vector": [1, 3, -1] + }, + "0.5": { + "vector": [2, 3, -2] + }, + "0.6": { + "vector": [2, 0, 0] + }, + "0.7": { + "vector": [2, 0, 0] + }, + "0.8": { + "vector": [2, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "right_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.2": { + "vector": [0, 0, 0] + }, + "0.5": { + "vector": [-12.5, 0, 0] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.75": { + "vector": [0, 0, 0] + }, + "1.2": { + "vector": [0, 0, 0] + } + } + }, + "axe": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.05": { + "vector": [-17.55366, -46.77638, 1.64566] + }, + "0.15": { + "vector": [-31.37378, -33.42989, -4.98105] + }, + "0.2": { + "vector": [-44.19581, -3.0647, 48.97981] + }, + "0.35": { + "vector": [-44.12388, 1.93476, 49.08245] + }, + "0.5": { + "vector": [-46.88452, -10.32122, 51.03982] + }, + "0.55": { + "vector": [-57.65366, -23.33192, 27.23873] + }, + "0.6": { + "vector": [16.98134, -43.05147, 42.21258] + }, + "1.25": { + "vector": [0, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.1": { + "vector": [0.8, -0.8, -2] + }, + "0.2": { + "vector": [0, 0, -1] + }, + "0.5": { + "vector": [0, 0, -1] + }, + "0.55": { + "vector": [0, 0, 0] + }, + "0.6": { + "vector": [0, 0, 0] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "left_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.3": { + "vector": [-2.5, 0, 0] + }, + "0.55": { + "vector": [12.5, 0, 0] + }, + "0.6": { + "vector": [12.5, 0, 0] + }, + "0.65": { + "vector": [12.5, 0, 0] + }, + "0.7": { + "vector": [12.5, 0, 0] + }, + "0.75": { + "vector": [12.5, 0, 0] + }, + "1.25": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, -1.5] + }, + "0.3": { + "vector": [0, -0.25, -1.5] + }, + "0.6": { + "vector": [0, -0.25, -1.5] + }, + "0.65": { + "vector": [0, -0.25, -1.5] + }, + "1.25": { + "vector": [0, -0.25, -1.5] + } + } + }, + "left_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "0.3": { + "vector": [5, 0, 0] + }, + "0.55": { + "vector": [15, 0, 0] + }, + "0.65": { + "vector": [15, 0, 0] + }, + "0.7": { + "vector": [15, 0, 0] + }, + "0.75": { + "vector": [15, 0, 0] + }, + "1.25": { + "vector": [5, 0, 0] + } + } + }, + "right_leg": { + "rotation": { + "0.0": { + "vector": [-2.5, 0, 0] + }, + "0.15": { + "vector": [-47.5, 0, 0] + }, + "0.4": { + "vector": [-47.5, 0, 0] + }, + "0.5": { + "vector": [-27.06, 0, 0] + }, + "1.25": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, -0.25, 0.5] + }, + "0.15": { + "vector": [0, -0.25, 0.5] + }, + "0.4": { + "vector": [0, -0.25, 0.5] + }, + "1.25": { + "vector": [0, -0.25, 0.5] + } + } + }, + "right_knee": { + "rotation": { + "0.0": { + "vector": [5, 0, 0] + }, + "0.15": { + "vector": [27.5, 0, 0] + }, + "0.4": { + "vector": [27.5, 0, 0] + }, + "0.5": { + "vector": [17.5, 0, 0] + }, + "0.65": { + "vector": [31.03, 0, 0] + }, + "1.25": { + "vector": [5, 0, 0] + } + } + }, + "h_head_furious": { + "rotation": { + "0.0": { + "vector": [0, -22.5, 0] + }, + "0.3": { + "vector": [5.24771, -55.29505, 5.70142] + }, + "0.5": { + "vector": [4.24777, -45.32881, 6.99588] + }, + "0.55": { + "vector": [2.01385, -45.16677, 0.61212] + }, + "0.6": { + "vector": [-31.34904, 12.54752, 4.35778] + }, + "0.75": { + "vector": [-37.83777, 14.32159, 7.88953] + }, + "1.25": { + "vector": [0, -22.5, 0] + } + } + }, + "inactive_axe": { + "scale": { + "vector": [0, 0, 0] + } + }, + "leg_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "h_slash10": { + "scale": { + "vector": [0, 0, 0] + } + }, + "body2": { + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.4": { + "vector": [0, 0, -1] + }, + "0.5": { + "vector": [0, -2, -7] + }, + "0.55": { + "vector": [0, -2, -9.5] + }, + "0.6": { + "vector": [0, -2, -10] + }, + "0.65": { + "vector": [0, -2, -11] + }, + "0.75": { + "vector": [0, -2, -11] + }, + "1.25": { + "vector": [0, 0, 0] + } + } + }, + "dreadnought_portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_normal": { + "scale": { + "vector": [0, 0, 0] + } + } + }, + "sound_effects": { + "0.6": { + "effect": "axe_hit" + } + } + }, + "death": { + "animation_length": 10, + "bones": { + "right_heel": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-12.5, 0, 0] + }, + "0.35": { + "vector": [17.5, 0, 0] + } + } + }, + "right_foot": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-33.13773, 0.2136, -1.9553] + }, + "0.35": { + "vector": [27.10718, 0.299, -2.7374] + }, + "0.6": { + "vector": [72.10718, 0.299, -2.7374] + } + } + }, + "left_heel": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-12.5, 0, 0] + }, + "0.35": { + "vector": [17.5, 0, 0] + } + } + }, + "left_foot": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-33.13773, -0.21358, 1.95528] + }, + "0.35": { + "vector": [27.10718, -0.29901, 2.73739] + }, + "0.6": { + "vector": [72.10718, -0.29901, 2.73739] + } + } + }, + "torso": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-35, 0, 0] + }, + "0.35": { + "vector": [-45, 0, 0] + }, + "0.45": { + "vector": [-45, 0, 0] + }, + "0.6": { + "vector": [22.5, 0, 0] + } + } + }, + "left_arm": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [-99.28275, -76.00637, 13.97749] + }, + "0.5": { + "vector": [-99.28275, -76.00637, 13.97749] + }, + "0.7": { + "vector": [-99.28275, -76.00637, 13.97749] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0] + } + } + }, + "left_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-2.5, 0, 0] + }, + "0.35": { + "vector": [12.5, 0, 0] + }, + "0.5": { + "vector": [17.5, 0, 0] + }, + "0.6": { + "vector": [17.5, 0, 0] + }, + "0.7": { + "vector": [17.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [-0.75, 0, 0] + }, + "0.5": { + "vector": [-0.75, 0, 0] + }, + "0.6": { + "vector": [0, 0, 0] + } + } + }, + "right_arm": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [-166.3778, 49.43206, -84.18841] + }, + "0.5": { + "vector": [-166.3778, 49.43206, -84.18841] + }, + "0.7": { + "vector": [-166.3778, 49.43206, -84.18841] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 0, 0] + } + } + }, + "right_elbow": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-2.5, 0, 0] + }, + "0.35": { + "vector": [12.5, 0, 0] + }, + "0.5": { + "vector": [17.5, 0, 0] + }, + "0.6": { + "vector": [17.5, 0, 0] + }, + "0.7": { + "vector": [17.5, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0.75, 0, 0] + }, + "0.5": { + "vector": [0.75, 0, 0] + }, + "0.6": { + "vector": [0, 0, 0] + } + } + }, + "axe": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.6": { + "vector": [-112.2725, 1.95838, -155.96286] + }, + "0.7": { + "vector": [-111.16188, -2.34128, -158.55673] + } + }, + "position": { + "0.6": { + "vector": [0, 0, 0] + }, + "0.7": { + "vector": [2.75, -16, -4.25] + }, + "0.75": { + "vector": [4.75, -18, -6.25] + } + } + }, + "left_leg": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, 7.5, 0] + }, + "0.6": { + "vector": [25, 7.5, 0] + } + } + }, + "left_knee": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [72.86, 0, 0] + }, + "0.35": { + "vector": [42.5, 0, 0] + }, + "0.6": { + "vector": [0, 0, 0] + } + } + }, + "right_leg": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [0, -7.5, 0] + }, + "0.6": { + "vector": [25, -7.5, 0] + } + } + }, + "right_knee": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [72.86, 0, 0] + }, + "0.35": { + "vector": [42.5, 0, 0] + }, + "0.6": { + "vector": [0, 0, 0] + } + } + }, + "h_head_furious": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [0, 0, 0] + }, + "0.4": { + "vector": [-22.5, 0, 0] + }, + "0.6": { + "vector": [-37.5, 0, 0] + }, + "0.75": { + "vector": [-12.5, 0, 0] + } + } + }, + "inactive_axe": { + "scale": { + "vector": [0, 0, 0] + } + }, + "leg_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "h_slash": { + "scale": { + "vector": [0, 0, 0] + } + }, + "body2": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [-7.5, 0, 0] + }, + "0.35": { + "vector": [27.5, 0, 0] + }, + "0.45": { + "vector": [65, 0, 0] + }, + "0.7": { + "vector": [65, 0, 0] + } + }, + "position": { + "0.0": { + "vector": [0, 0, 0] + }, + "0.25": { + "vector": [0, -8.5, -6] + }, + "0.35": { + "vector": [0, -10, -10] + }, + "0.45": { + "vector": [0, -19, -14] + } + } + }, + "dreadnought_portal": { + "scale": { + "vector": [0, 0, 0] + } + }, + "eyes_attack": { + "scale": { + "vector": [0, 0, 0] + } + } + } + }, + "spawn": { + "animation_length": 14.35, + "override_previous_animation": true, + "bones": { + "dreadnought": { + "position": { + "0.0": { + "vector": [0, 0, 90] + }, + "0.3": { + "vector": [0, 0, 90] + }, + "0.35": { + "vector": [0, 0, 80] + }, + "2.35": { + "vector": [0, 0, 64] + }, + "10.35": { + "vector": [0, 0, 32] + }, + "14.35": { + "vector": [0, 0, 0] + } + }, + "scale": { + "0.3": { + "vector": [0, 0, 0] + }, + "0.35": { + "vector": [1, 1, 1] + } + } + }, + "torso": { + "rotation": { + "0.35": { + "vector": [0, 0, 0] + }, + "9.35": { + "vector": [0, 0, 0] + }, + "10.35": { + "vector": [0, 22.5, 0] + }, + "12.35": { + "vector": [0, 22.5, 0] + }, + "14.35": { + "vector": [0, 22.5, 0] + } + }, + "position": { + "0.35": { + "vector": [-0.25, 0, 0] + }, + "0.8": { + "vector": [-0.33, 0, 0] + }, + "1.6": { + "vector": [0.25, 0, 0] + }, + "2.05": { + "vector": [0.33, 0, 0] + }, + "2.85": { + "vector": [-0.25, 0, 0] + }, + "3.3": { + "vector": [-0.33, 0, 0] + }, + "4.1": { + "vector": [0.25, 0, 0] + }, + "4.55": { + "vector": [0.33, 0, 0] + }, + "5.35": { + "vector": [-0.25, 0, 0] + }, + "5.8": { + "vector": [-0.33, 0, 0] + }, + "6.6": { + "vector": [0.25, 0, 0] + }, + "7.05": { + "vector": [0.33, 0, 0] + }, + "7.85": { + "vector": [-0.25, 0, 0] + }, + "8.3": { + "vector": [-0.33, 0, 0] + }, + "9.1": { + "vector": [0.25, 0, 0] + }, + "9.55": { + "vector": [0.33, 0, 0] + }, + "10.35": { + "vector": [-0.25, 0, 0] + }, + "10.65": { + "vector": [-0.33, 0, 0] + }, + "11.35": { + "vector": [0.25, 0, 0] + }, + "11.65": { + "vector": [0.33, 0, 0] + }, + "12.35": { + "vector": [-0.25, 0, 0] + }, + "12.65": { + "vector": [-0.33, 0, 0] + }, + "13.35": { + "vector": [0.25, 0, 0] + }, + "13.65": { + "vector": [0.33, 0, 0] + }, + "14.35": { + "vector": [0, 0, 0] + } + } + }, + "upper_torso": { + "rotation": { + "0.35": { + "vector": [0, 0, 1] + }, + "0.8": { + "vector": [0, 0, 2.5] + }, + "1.6": { + "vector": [0, 0, -1] + }, + "2.05": { + "vector": [0, 0, -2.5] + }, + "2.85": { + "vector": [0, 0, 1] + }, + "3.3": { + "vector": [0, 0, 2.5] + }, + "4.1": { + "vector": [0, 0, -1] + }, + "4.55": { + "vector": [0, 0, -2.5] + }, + "5.35": { + "vector": [0, 0, 1] + }, + "5.8": { + "vector": [0, 0, 2.5] + }, + "6.6": { + "vector": [0, 0, -1] + }, + "7.05": { + "vector": [0, 0, -2.5] + }, + "7.85": { + "vector": [0, 0, 1] + }, + "8.3": { + "vector": [0, 0, 2.5] + }, + "9.1": { + "vector": [0, 0, -1] + }, + "9.55": { + "vector": [0, 0, -2.5] + }, + "10.35": { + "vector": [0, 0, 1] + }, + "10.65": { + "vector": [0, 0, 2.5] + }, + "11.35": { + "vector": [0, 0, -1] + }, + "11.65": { + "vector": [0, 0, -2.5] + }, + "12.35": { + "vector": [0, 0, 1] + }, + "12.65": { + "vector": [0, 0, 2.5] + }, + "13.35": { + "vector": [0, 0, -1] + }, + "13.65": { + "vector": [0, 0, -2.5] + }, + "14.35": { + "vector": [0, 0, 0] + } + }, + "position": { + "0.35": { + "vector": [0, 0, 0] + }, + "0.5": { + "vector": [0, -0.17, 0] + }, + "0.65": { + "vector": [0, -0.83, 0] + }, + "0.8": { + "vector": [0, -1, 0] + }, + "0.95": { + "vector": [0, -0.93, 0] + }, + "1.4": { + "vector": [0, -0.07, 0] + }, + "1.6": { + "vector": [0, 0, 0] + }, + "1.75": { + "vector": [0, -0.17, 0] + }, + "1.9": { + "vector": [0, -0.83, 0] + }, + "2.05": { + "vector": [0, -1, 0] + }, + "2.2": { + "vector": [0, -0.93, 0] + }, + "2.7": { + "vector": [0, -0.07, 0] + }, + "2.85": { + "vector": [0, 0, 0] + }, + "3.0": { + "vector": [0, -0.17, 0] + }, + "3.15": { + "vector": [0, -0.83, 0] + }, + "3.3": { + "vector": [0, -1, 0] + }, + "3.45": { + "vector": [0, -0.93, 0] + }, + "3.9": { + "vector": [0, -0.07, 0] + }, + "4.1": { + "vector": [0, 0, 0] + }, + "4.25": { + "vector": [0, -0.17, 0] + }, + "4.4": { + "vector": [0, -0.83, 0] + }, + "4.55": { + "vector": [0, -1, 0] + }, + "4.7": { + "vector": [0, -0.93, 0] + }, + "5.2": { + "vector": [0, -0.07, 0] + }, + "5.35": { + "vector": [0, 0, 0] + }, + "5.5": { + "vector": [0, -0.17, 0] + }, + "5.65": { + "vector": [0, -0.83, 0] + }, + "5.8": { + "vector": [0, -1, 0] + }, + "5.95": { + "vector": [0, -0.93, 0] + }, + "6.4": { + "vector": [0, -0.07, 0] + }, + "6.6": { + "vector": [0, 0, 0] + }, + "6.75": { + "vector": [0, -0.17, 0] + }, + "6.9": { + "vector": [0, -0.83, 0] + }, + "7.05": { + "vector": [0, -1, 0] + }, + "7.2": { + "vector": [0, -0.93, 0] + }, + "7.7": { + "vector": [0, -0.07, 0] + }, + "7.85": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -0.17, 0] + }, + "8.15": { + "vector": [0, -0.83, 0] + }, + "8.3": { + "vector": [0, -1, 0] + }, + "8.45": { + "vector": [0, -0.93, 0] + }, + "8.9": { + "vector": [0, -0.07, 0] + }, + "9.1": { + "vector": [0, 0, 0] + }, + "9.25": { + "vector": [0, -0.17, 0] + }, + "9.4": { + "vector": [0, -0.83, 0] + }, + "9.55": { + "vector": [0, -1, 0] + }, + "9.7": { + "vector": [0, -0.93, 0] + }, + "10.2": { + "vector": [0, -0.07, 0] + }, + "10.35": { + "vector": [0, 0, 0] + }, + "10.45": { + "vector": [0, -0.17, 0] + }, + "10.55": { + "vector": [0, -0.83, 0] + }, + "10.65": { + "vector": [0, -1, 0] + }, + "10.8": { + "vector": [0, -0.93, 0] + }, + "11.2": { + "vector": [0, -0.07, 0] + }, + "11.35": { + "vector": [0, 0, 0] + }, + "11.45": { + "vector": [0, -0.17, 0] + }, + "11.55": { + "vector": [0, -0.83, 0] + }, + "11.65": { + "vector": [0, -1, 0] + }, + "11.8": { + "vector": [0, -0.93, 0] + }, + "12.2": { + "vector": [0, -0.07, 0] + }, + "12.35": { + "vector": [0, 0, 0] + }, + "12.45": { + "vector": [0, -0.17, 0] + }, + "12.55": { + "vector": [0, -0.83, 0] + }, + "12.65": { + "vector": [0, -1, 0] + }, + "12.8": { + "vector": [0, -0.93, 0] + }, + "13.2": { + "vector": [0, -0.07, 0] + }, + "13.35": { + "vector": [0, 0, 0] + }, + "13.45": { + "vector": [0, -0.17, 0] + }, + "13.55": { + "vector": [0, -0.83, 0] + }, + "13.65": { + "vector": [0, -1, 0] + }, + "13.8": { + "vector": [0, -0.93, 0] + }, + "14.2": { + "vector": [0, -0.07, 0] + }, + "14.35": { + "vector": [0, 0, 0] + } + } + }, + "left_arm": { + "rotation": { + "0.35": { + "vector": [-65.3438, 2.97085, -54.00316] + }, + "1.35": { + "vector": [-65.3438, 2.97085, -54.00316] + }, + "2.1": { + "vector": [-19.29063, 7.22373, -42.19927] + }, + "2.6": { + "vector": [13.14, 0.83582, -11.7567] + }, + "3.45": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "5.35": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "6.6": { + "vector": [12.5, 0, 0] + }, + "7.4": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "7.85": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "9.1": { + "vector": [12.5, 0, 0] + }, + "9.9": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "10.35": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "14.35": { + "vector": [0, 0, 0] + } + } + }, + "right_arm": { + "rotation": { + "0.35": { + "vector": [12.68664, -9.76061, -2.18558] + }, + "2.85": { + "vector": [12.68664, -9.76061, -2.18558] + }, + "3.55": { + "vector": [-10.15108, -9.84655, 1.75378] + }, + "4.1": { + "vector": [-10.08648, -8.98092, 6.66735] + }, + "5.35": { + "vector": [12.68664, -9.76061, -2.18558] + }, + "6.05": { + "vector": [-10.15108, -9.84655, 1.75378] + }, + "6.6": { + "vector": [-10.08648, -8.98092, 6.66735] + }, + "7.85": { + "vector": [12.68664, -9.76061, -2.18558] + }, + "8.2": { + "vector": [37.68664, -9.76061, -2.18558] + }, + "8.35": { + "vector": [20.18664, -9.76061, -2.18558] + }, + "9.35": { + "vector": [25.0691, 18.40276, 1.7084] + }, + "10.35": { + "vector": [36.94319, 29.52361, 9.71415] + }, + "10.65": { + "vector": [37.94, 29.52, 9.71] + }, + "11.35": { + "vector": [37.1047, 28.17926, 12.12078] + }, + "11.65": { + "vector": [36.1047, 28.17926, 12.12078] + }, + "12.35": { + "vector": [36.94319, 29.52361, 9.71415] + }, + "12.65": { + "vector": [37.94, 29.52, 9.71] + }, + "13.35": { + "vector": [37.1047, 28.17926, 12.12078] + }, + "13.65": { + "vector": [36.1047, 28.17926, 12.12078] + }, + "14.35": { + "vector": [36.94319, 29.52361, 9.71415] + } + }, + "position": { + "7.85": { + "vector": [0, 0, 0] + }, + "8.2": { + "vector": [0, 1, 1] + }, + "8.35": { + "vector": [-1, -1, -1] + }, + "9.35": { + "vector": [0, 0, 0] + }, + "10.35": { + "vector": [0, 0, 0] + }, + "10.45": { + "vector": [0, 0.17, 0] + }, + "10.55": { + "vector": [0, 0.83, 0] + }, + "10.65": { + "vector": [0, 1, 0] + }, + "10.8": { + "vector": [0, 0.93, 0] + }, + "11.2": { + "vector": [0, 0.07, 0] + }, + "11.35": { + "vector": [0, 0, 0] + }, + "11.45": { + "vector": [0, 0.17, 0] + }, + "11.55": { + "vector": [0, 0.83, 0] + }, + "11.65": { + "vector": [0, 1, 0] + }, + "11.8": { + "vector": [0, 0.93, 0] + }, + "12.2": { + "vector": [0, 0.07, 0] + }, + "12.35": { + "vector": [0, 0, 0] + }, + "12.45": { + "vector": [0, 0.17, 0] + }, + "12.55": { + "vector": [0, 0.83, 0] + }, + "12.65": { + "vector": [0, 1, 0] + }, + "12.8": { + "vector": [0, 0.93, 0] + }, + "13.2": { + "vector": [0, 0.07, 0] + }, + "13.35": { + "vector": [0, 0, 0] + }, + "13.45": { + "vector": [0, 0.17, 0] + }, + "13.55": { + "vector": [0, 0.83, 0] + }, + "13.65": { + "vector": [0, 1, 0] + }, + "13.8": { + "vector": [0, 0.93, 0] + }, + "14.2": { + "vector": [0, 0.07, 0] + }, + "14.35": { + "vector": [0, 0, 0] + } + } + }, + "right_elbow": { + "rotation": { + "0.35": { + "vector": [22.5, 0, 0] + }, + "2.85": { + "vector": [22.5, 0, 0] + }, + "5.35": { + "vector": [22.5, 0, 0] + }, + "7.85": { + "vector": [22.5, 0, 0] + }, + "8.1": { + "vector": [-2.5, 0, 0] + }, + "8.25": { + "vector": [-2.5, 0, 0] + }, + "8.4": { + "vector": [2.5, 0, 0] + }, + "9.35": { + "vector": [22.5, 0, 0] + }, + "10.35": { + "vector": [0, 0, 0] + } + } + }, + "axe": { + "rotation": { + "8.4": { + "vector": [-17.76543, -17.59014, -11.04134] + }, + "8.45": { + "vector": [-17.76543, -17.59014, -11.04134] + }, + "8.85": { + "vector": [-17.76543, -17.59014, -11.04134] + }, + "9.35": { + "vector": [-26.84362, -11.72676, -7.36089] + }, + "10.35": { + "vector": [0, 0, 0] + } + }, + "position": { + "8.35": { + "vector": [0.75, 3, 3] + }, + "8.4": { + "vector": [0.75, 3, 3] + }, + "8.45": { + "vector": [0.75, 3, 3] + }, + "8.85": { + "vector": [0.75, 3, 3] + }, + "10.35": { + "vector": [0, 0, 0] + } + }, + "scale": { + "8.35": { + "vector": [0, 0, 0] + }, + "8.4": { + "vector": [1, 1, 1] + } + } + }, + "left_leg": { + "rotation": { + "0.35": { + "vector": [5, 0, 0] + }, + "1.05": { + "vector": [-10, 0, 0] + }, + "1.6": { + "vector": [-9.96271, 0.86717, 4.92442] + }, + "2.85": { + "vector": [5, 0, 0] + }, + "3.55": { + "vector": [-10, 0, 0] + }, + "4.1": { + "vector": [-9.96271, 0.86717, 4.92442] + }, + "5.35": { + "vector": [5, 0, 0] + }, + "6.05": { + "vector": [-10, 0, 0] + }, + "6.6": { + "vector": [-9.96271, 0.86717, 4.92442] + }, + "7.85": { + "vector": [5, 0, 0] + }, + "8.55": { + "vector": [-10, 0, 0] + }, + "9.1": { + "vector": [-9.96271, 0.86717, 4.92442] + }, + "10.35": { + "vector": [12.5, 0, 0] + }, + "10.9": { + "vector": [-10, 0, 0] + }, + "11.35": { + "vector": [-9.96271, 0.86717, 4.92442] + }, + "12.35": { + "vector": [12.5, 0, 0] + }, + "12.9": { + "vector": [-10, 0, 0] + }, + "13.35": { + "vector": [-9.96271, 0.86717, 4.92442] + }, + "14.35": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.35": { + "vector": [0, -0.5, -1] + }, + "1.05": { + "vector": [0, 0, -2] + }, + "1.6": { + "vector": [0, -1, -2.5] + }, + "2.85": { + "vector": [0, -0.5, -1] + }, + "3.55": { + "vector": [0, 0, -2] + }, + "4.1": { + "vector": [0, -1, -2.5] + }, + "5.35": { + "vector": [0, -0.5, -1] + }, + "6.05": { + "vector": [0, 0, -2] + }, + "6.6": { + "vector": [0, -1, -2.5] + }, + "7.85": { + "vector": [0, -0.5, -1] + }, + "8.55": { + "vector": [0, 0, -2] + }, + "9.1": { + "vector": [0, -1, -2.5] + }, + "10.35": { + "vector": [0, -0.5, -1] + }, + "10.5": { + "vector": [0, 1, -2] + }, + "10.9": { + "vector": [0, 1, -2] + }, + "11.35": { + "vector": [0, -1, -2.5] + }, + "12.35": { + "vector": [0, -0.5, -1] + }, + "12.5": { + "vector": [0, 1, -2] + }, + "12.9": { + "vector": [0, 1, -2] + }, + "13.35": { + "vector": [0, -1, -2.5] + }, + "14.35": { + "vector": [0, -0.25, -1.5] + } + } + }, + "left_knee": { + "rotation": { + "0.35": { + "vector": [0, 0, 0] + }, + "0.8": { + "vector": [32.5, 0, 0] + }, + "1.6": { + "vector": [5, 0, 0] + }, + "2.85": { + "vector": [0, 0, 0] + }, + "3.3": { + "vector": [32.5, 0, 0] + }, + "4.1": { + "vector": [5, 0, 0] + }, + "5.35": { + "vector": [0, 0, 0] + }, + "5.8": { + "vector": [32.5, 0, 0] + }, + "6.6": { + "vector": [5, 0, 0] + }, + "7.85": { + "vector": [0, 0, 0] + }, + "8.3": { + "vector": [32.5, 0, 0] + }, + "9.1": { + "vector": [5, 0, 0] + }, + "10.35": { + "vector": [0, 0, 0] + }, + "10.65": { + "vector": [32.5, 0, 0] + }, + "11.35": { + "vector": [5, 0, 0] + }, + "12.35": { + "vector": [0, 0, 0] + }, + "12.65": { + "vector": [32.5, 0, 0] + }, + "13.35": { + "vector": [5, 0, 0] + }, + "14.35": { + "vector": [5, 0, 0] + } + } + }, + "right_leg": { + "rotation": { + "0.35": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "1.6": { + "vector": [5, 0, 0] + }, + "2.4": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "2.85": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "4.1": { + "vector": [5, 0, 0] + }, + "4.9": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "5.35": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "6.6": { + "vector": [5, 0, 0] + }, + "7.4": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "7.85": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "9.1": { + "vector": [5, 0, 0] + }, + "9.9": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "10.35": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "11.35": { + "vector": [12.5, 0, 0] + }, + "11.9": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "12.35": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "13.35": { + "vector": [12.5, 0, 0] + }, + "13.9": { + "vector": [-9.96271, -0.86717, -4.92442] + }, + "14.35": { + "vector": [-2.5, 0, 0] + } + }, + "position": { + "0.35": { + "vector": [0, -1, -0.5] + }, + "1.6": { + "vector": [0, -0.5, 1] + }, + "2.4": { + "vector": [0, 0, 0] + }, + "2.85": { + "vector": [0, -1, -0.5] + }, + "4.1": { + "vector": [0, -0.5, 1] + }, + "4.9": { + "vector": [0, 0, 0] + }, + "5.35": { + "vector": [0, -1, -0.5] + }, + "6.6": { + "vector": [0, -0.5, 1] + }, + "7.4": { + "vector": [0, 0, 0] + }, + "7.85": { + "vector": [0, -1, -0.5] + }, + "9.1": { + "vector": [0, -0.5, 1] + }, + "9.9": { + "vector": [0, 0, 0] + }, + "10.35": { + "vector": [0, -1, -0.5] + }, + "11.35": { + "vector": [0, -0.5, 1] + }, + "11.5": { + "vector": [0, 1, 0] + }, + "11.9": { + "vector": [0, 1, 0] + }, + "12.35": { + "vector": [0, -1, -0.5] + }, + "13.35": { + "vector": [0, -0.5, 1] + }, + "13.5": { + "vector": [0, 1, 0] + }, + "13.9": { + "vector": [0, 1, 0] + }, + "14.35": { + "vector": [0, -0.25, 0.5] + } + } + }, + "right_knee": { + "rotation": { + "0.35": { + "vector": [5, 0, 0] + }, + "1.6": { + "vector": [0, 0, 0] + }, + "2.05": { + "vector": [32.5, 0, 0] + }, + "2.85": { + "vector": [5, 0, 0] + }, + "4.1": { + "vector": [0, 0, 0] + }, + "4.55": { + "vector": [32.5, 0, 0] + }, + "5.35": { + "vector": [5, 0, 0] + }, + "6.6": { + "vector": [0, 0, 0] + }, + "7.05": { + "vector": [32.5, 0, 0] + }, + "7.85": { + "vector": [5, 0, 0] + }, + "9.1": { + "vector": [0, 0, 0] + }, + "9.55": { + "vector": [32.5, 0, 0] + }, + "10.35": { + "vector": [5, 0, 0] + }, + "11.35": { + "vector": [0, 0, 0] + }, + "11.65": { + "vector": [32.5, 0, 0] + }, + "12.35": { + "vector": [5, 0, 0] + }, + "13.35": { + "vector": [0, 0, 0] + }, + "13.65": { + "vector": [32.5, 0, 0] + }, + "14.35": { + "vector": [5, 0, 0] + } + } + }, + "inactive_axe": { + "rotation": { + "0.35": { + "vector": [2.51565, -47.38422, -0.7522] + }, + "2.85": { + "vector": [-32.48435, -47.38422, -0.7522] + }, + "3.55": { + "vector": [-17.48435, -47.38422, -0.7522] + }, + "4.1": { + "vector": [-19.98435, -47.38422, -0.7522] + }, + "5.35": { + "vector": [-32.48435, -47.38422, -0.7522] + }, + "6.05": { + "vector": [-17.48435, -47.38422, -0.7522] + }, + "6.6": { + "vector": [-19.98435, -47.38422, -0.7522] + }, + "7.85": { + "vector": [-17.76543, -17.59014, -11.04134] + }, + "8.15": { + "vector": [-17.76543, -17.59014, -11.04134] + }, + "8.35": { + "vector": [-17.76543, -17.59014, -11.04134] + } + }, + "position": { + "0.35": { + "vector": [0, 7, 7] + }, + "2.85": { + "vector": [0, 1, 4] + }, + "3.55": { + "vector": [0, 2, 4] + }, + "4.1": { + "vector": [0, 1, 4] + }, + "5.35": { + "vector": [0, 1, 4] + }, + "6.05": { + "vector": [-1, 2.75, 4] + }, + "6.6": { + "vector": [-1, 2, 4] + }, + "7.85": { + "vector": [0.75, 3, 3] + }, + "8.15": { + "vector": [0.75, 3, 3] + }, + "8.35": { + "vector": [0.75, 3, 3] + }, + "8.4": { + "vector": [0, -3200, 4] + } + }, + "scale": { + "0.0": { + "vector": [1, 1, 1] + }, + "8.35": { + "vector": [1, 1, 1] + }, + "8.45": { + "vector": [0, 0, 0] + } + } + }, + "h_head_furious": { + "rotation": { + "0.35": { + "vector": [0, 0, -1] + }, + "0.8": { + "vector": [15, 0, 1] + }, + "1.6": { + "vector": [15, 0, 1] + }, + "2.85": { + "vector": [0, 0, -1] + }, + "3.3": { + "vector": [0, 0, -2.5] + }, + "4.1": { + "vector": [0, 0, 1] + }, + "4.55": { + "vector": [0, 0, 2.5] + }, + "5.35": { + "vector": [0, 0, -1] + }, + "5.8": { + "vector": [0, 0, -2.5] + }, + "6.6": { + "vector": [0, 0, 1] + }, + "7.05": { + "vector": [0, 0, 2.5] + }, + "7.85": { + "vector": [0, 0, -1] + }, + "8.3": { + "vector": [0, 0, -2.5] + }, + "9.1": { + "vector": [0, 0, 1] + }, + "9.55": { + "vector": [0, 0, 2.5] + }, + "10.35": { + "vector": [0, -22.5, -1] + }, + "10.65": { + "vector": [0, -22.5, -2.5] + }, + "11.35": { + "vector": [0, -22.5, 1] + }, + "11.65": { + "vector": [0, -22.5, 2.5] + }, + "12.35": { + "vector": [0, -22.5, -1] + }, + "12.65": { + "vector": [0, -22.5, -2.5] + }, + "13.35": { + "vector": [0, -22.5, 1] + }, + "13.65": { + "vector": [0, -22.5, 2.5] + }, + "14.35": { + "vector": [0, -22.5, 0] + } + }, + "position": { + "0.35": { + "vector": [0, 0, 0] + }, + "1.35": { + "vector": [0, 0, -2] + }, + "1.75": { + "vector": [0, 0, -2] + }, + "2.05": { + "vector": [0, 0, 0] + }, + "14.35": { + "vector": [0, 0, 0] + } + } + }, + "hand_boomstick": { + "scale": { + "vector": [0, 0, 0] + } + }, + "h_slash": { + "scale": { + "vector": [0, 0, 0] + } + }, + "body2": { + "position": { + "vector": [0, 0, 0] + } + }, + "dreadnought_portal": { + "position": { + "0.0": { + "vector": [0, 0, 69] + }, + "7.85": { + "vector": [0, 0, 69] + }, + "8.0": { + "vector": [0, 25, 69] + } + }, + "scale": { + "7.85": { + "vector": [1, 1, 1] + }, + "8.1": { + "vector": [0, 0, 0] + } + } + }, + "black": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cluster1": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud1": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud2": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud3": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud4": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster2": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud5": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud6": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud7": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud8": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud9": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster3": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud10": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud11": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud12": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud13": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster4": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud14": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud15": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud16": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud17": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud18": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster5": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud19": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud20": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud21": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud22": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster6": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud23": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud24": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud25": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud26": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud27": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster7": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud28": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud29": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud30": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud31": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster8": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud32": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud33": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud34": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud35": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud36": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster9": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud37": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud38": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud39": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud40": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster10": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud41": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud42": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud43": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud44": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud45": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster11": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud46": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud47": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud48": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud49": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster12": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud50": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud51": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud52": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud53": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud54": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster13": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud55": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud56": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud57": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud58": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster14": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud59": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud60": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud61": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud62": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud63": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster15": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud64": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud65": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud66": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud67": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster16": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud68": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud69": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud70": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud71": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud72": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster17": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud73": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud74": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud75": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud76": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster18": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud77": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud78": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud79": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud80": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud81": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster19": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud82": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud83": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud84": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud85": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster20": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud86": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud87": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud88": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud89": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud90": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster21": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud91": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud92": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud93": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud94": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster22": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud95": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud96": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud97": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud98": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud99": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster23": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud100": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud101": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud102": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud103": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster24": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud104": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud105": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud106": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud107": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud108": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster25": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud109": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud110": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud111": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud112": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster26": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud113": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud114": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud115": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud116": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud117": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster27": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud118": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud119": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud120": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud121": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster28": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud122": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud123": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud124": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud125": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud126": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster29": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud127": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud128": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud129": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud130": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster30": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud131": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud132": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud133": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud134": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud135": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cluster31": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud136": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 360, 0] + } + } + }, + "cloud137": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud138": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 960, 0] + } + } + }, + "cloud139": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 2880, 0] + } + } + }, + "cluster32": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 0, -360] + } + } + }, + "cloud140": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -2880, 0] + } + } + }, + "cloud141": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud142": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, 1440, 0] + } + } + }, + "cloud143": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "cloud144": { + "rotation": { + "0.0": { + "vector": [0, 0, 0] + }, + "8.0": { + "vector": [0, -1440, 0] + } + } + }, + "eyes_attack": { + "scale": { + "vector": [0, 0, 0] + } + } + }, + "sound_effects": { + "0.0": { + "effect": "portal" + }, + "0.35": { + "effect": "walk" + }, + "1.7": { + "effect": "walk" + }, + "2.85": { + "effect": "walk" + }, + "4.05": { + "effect": "walk" + }, + "4.3": { + "effect": "portal" + }, + "5.25": { + "effect": "walk" + }, + "6.5": { + "effect": "walk" + }, + "7.85": { + "effect": "walk" + }, + "8.35": { + "effect": "axe" + }, + "9.05": { + "effect": "walk" + }, + "10.3": { + "effect": "walk" + }, + "11.2": { + "effect": "walk" + }, + "12.25": { + "effect": "walk" + }, + "13.25": { + "effect": "walk" + }, + "14.0": { + "effect": "walk" + } + } + } + }, + "azurelib_format_version": 2 +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/animations/item/doomicorn.animation.json b/common/src/main/resources/assets/azurelib/animations/item/doomicorn.animation.json new file mode 100644 index 000000000..c7d87a8e1 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/animations/item/doomicorn.animation.json @@ -0,0 +1,741 @@ +{ + "format_version": "1.8.0", + "animations": { + "equipping": { + "loop": true, + "animation_length": 1.84, + "bones": { + "bipedHead": { + "rotation": { + "0.0": { + "vector": [ + -90, + 0, + 0 + ] + }, + "0.76": { + "vector": [ + -78.65, + 0, + 0 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "position": { + "0.0": { + "vector": [ + 0, + 0, + 2 + ] + }, + "0.76": { + "vector": [ + 0, + 10, + 15.03 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "scale": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "1.56": { + "vector": [ + 1, + 1, + 1 + ], + "easing": "easeInOutSine" + } + } + }, + "bipedBody": { + "rotation": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "0.76": { + "vector": [ + 0, + 359, + 0 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "position": { + "0.0": { + "vector": [ + 0, + 0, + 2 + ] + }, + "0.76": { + "vector": [ + 0, + 10, + 15.03 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "scale": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "1.56": { + "vector": [ + 1, + 1, + 1 + ], + "easing": "easeInOutSine" + } + } + }, + "bipedRightArm": { + "rotation": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "0.76": { + "vector": [ + 0, + 359, + 0 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "position": { + "0.0": { + "vector": [ + 0, + 0, + 2 + ] + }, + "0.76": { + "vector": [ + -15, + 10, + -1.97 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "scale": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "1.56": { + "vector": [ + 1, + 1, + 1 + ], + "easing": "easeInOutSine" + } + } + }, + "bipedLeftArm": { + "rotation": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "0.76": { + "vector": [ + 0, + 359, + 0 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "position": { + "0.0": { + "vector": [ + 0, + 0, + 2 + ] + }, + "0.76": { + "vector": [ + 15, + 10, + -1.97 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "scale": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "1.56": { + "vector": [ + 1, + 1, + 1 + ], + "easing": "easeInOutSine" + } + } + }, + "bipedRightLeg": { + "rotation": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "0.76": { + "vector": [ + 0, + 359, + 0 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "position": { + "0.0": { + "vector": [ + 0, + 0, + 2 + ] + }, + "0.76": { + "vector": [ + 11, + -31, + 15.03 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "scale": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "1.56": { + "vector": [ + 1, + 1, + 1 + ], + "easing": "easeInOutSine" + } + } + }, + "bipedLeftLeg": { + "rotation": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "0.76": { + "vector": [ + 0, + 359, + 0 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "position": { + "0.0": { + "vector": [ + 0, + 0, + 2 + ] + }, + "0.76": { + "vector": [ + -11, + -31, + 15.03 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "scale": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "1.56": { + "vector": [ + 1, + 1, + 1 + ], + "easing": "easeInOutSine" + } + } + } + } + }, + "idle": { + "loop": true, + "animation_length": 4.6, + "bones": { + "group": { + "rotation": { + "0.0": { + "vector": [ + 0, + -20, + 0 + ] + }, + "0.8": { + "vector": [ + 0, + -15, + 15 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + -20, + 0 + ], + "easing": "easeInOutSine" + }, + "2.32": { + "vector": [ + 0, + -15, + 15 + ], + "easing": "easeInOutSine" + }, + "3.08": { + "vector": [ + 0, + -20, + 0 + ], + "easing": "easeInOutSine" + }, + "3.84": { + "vector": [ + 0, + -15, + 15 + ], + "easing": "easeInOutSine" + }, + "4.6": { + "vector": [ + 0, + -20, + 0 + ], + "easing": "easeInOutSine" + } + } + }, + "group2": { + "rotation": { + "0.0": { + "vector": [ + 0, + -20, + 0 + ] + }, + "0.8": { + "vector": [ + 0, + -15, + -15 + ], + "easing": "easeInOutSine" + }, + "1.56": { + "vector": [ + 0, + -20, + 0 + ], + "easing": "easeInOutSine" + }, + "2.32": { + "vector": [ + 0, + -15, + -15 + ], + "easing": "easeInOutSine" + }, + "3.08": { + "vector": [ + 0, + -20, + 0 + ], + "easing": "easeInOutSine" + }, + "3.84": { + "vector": [ + 0, + -15, + -15 + ], + "easing": "easeInOutSine" + }, + "4.6": { + "vector": [ + 0, + -20, + 0 + ], + "easing": "easeInOutSine" + } + } + }, + "launcher": { + "rotation": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "0.52": { + "vector": [ + 25, + 0, + 0 + ], + "easing": "easeInOutSine" + }, + "4.04": { + "vector": [ + 25, + 0, + 0 + ], + "easing": "easeInOutSine" + }, + "4.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + } + }, + "bone4": { + "rotation": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "0.52": { + "vector": [ + -25, + 0, + 0 + ], + "easing": "easeInOutSine" + }, + "1.0": { + "vector": [ + -25, + -17.5, + 0 + ], + "easing": "easeInOutSine" + }, + "1.52": { + "vector": [ + -25, + 0, + 0 + ], + "easing": "easeInOutSine" + }, + "2.0": { + "vector": [ + -25, + 13, + 0 + ], + "easing": "easeInOutSine" + }, + "2.52": { + "vector": [ + -25, + 0, + 0 + ], + "easing": "easeInOutSine" + }, + "3.04": { + "vector": [ + -25, + -17.5, + 0 + ], + "easing": "easeInOutSine" + }, + "3.52": { + "vector": [ + -25, + 0, + 0 + ], + "easing": "easeInOutSine" + }, + "4.08": { + "vector": [ + -25, + 0, + 0 + ], + "easing": "easeInOutSine" + }, + "4.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + } + } + } + }, + "attacking": { + "animation_length": 2.08, + "bones": { + "blade": { + "position": { + "0.0": { + "vector": [ + 5, + 0, + 0 + ] + }, + "0.52": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutCirc" + }, + "1.56": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutCirc" + }, + "2.08": { + "vector": [ + 5, + 0, + 0 + ], + "easing": "easeInOutCirc" + } + }, + "scale": { + "0.0": { + "vector": [ + 0.7, + 0.7, + 0.7 + ] + }, + "0.52": { + "vector": [ + 1, + 1, + 1 + ], + "easing": "easeInOutCirc" + }, + "1.56": { + "vector": [ + 1, + 1, + 1 + ], + "easing": "easeInOutCirc" + }, + "2.08": { + "vector": [ + 0.7, + 0.7, + 0.7 + ], + "easing": "easeInOutCirc" + } + } + } + } + } + }, + "geckolib_format_version": 2 +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/animations/item/pistol.animation.json b/common/src/main/resources/assets/azurelib/animations/item/pistol.animation.json new file mode 100644 index 000000000..dba595e89 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/animations/item/pistol.animation.json @@ -0,0 +1,80 @@ +{ + "format_version": "1.8.0", + "animations": { + "firing": { + "loop": true, + "animation_length": 0.16, + "bones": { + "group": { + "rotation": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "0.08": { + "vector": [ + -7.5, + 0, + 0 + ], + "easing": "easeInOutSine" + }, + "0.16": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + }, + "position": { + "0.0": { + "vector": [ + 0, + 0, + 0 + ] + }, + "0.08": { + "vector": [ + 0, + 0, + 2 + ], + "easing": "easeInOutSine" + }, + "0.16": { + "vector": [ + 0, + 0, + 0 + ], + "easing": "easeInOutSine" + } + } + }, + "bone": { + "position": { + "vector": [ + 0, + 0, + -0.3 + ] + }, + "scale": { + "vector": [ + 7, + 7, + 7 + ] + } + } + } + } + }, + "geckolib_format_version": 2 +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/geo/block/stargate.geo.json b/common/src/main/resources/assets/azurelib/geo/block/stargate.geo.json new file mode 100644 index 000000000..8da51fbc9 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/geo/block/stargate.geo.json @@ -0,0 +1,877 @@ +{ + "format_version": "1.21.20", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.unknown", + "texture_width": 256, + "texture_height": 256, + "visible_bounds_width": 7, + "visible_bounds_height": 6.5, + "visible_bounds_offset": [0, 2.75, 0] + }, + "bones": [ + { + "name": "bone9", + "pivot": [0, 12, -0.6] + }, + { + "name": "outter_ring", + "parent": "bone9", + "pivot": [0, 40, 0], + "cubes": [ + { + "origin": [-7.95649, 0, -3], + "size": [15.91299, 4, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, -45], + "uv": { + "north": {"uv": [160, 24], "uv_size": [16, 4]}, + "east": {"uv": [42, 140], "uv_size": [6, 4]}, + "south": {"uv": [160, 28], "uv_size": [16, 4]}, + "west": {"uv": [122, 161], "uv_size": [6, 4]}, + "up": {"uv": [26, 132], "uv_size": [16, 6]}, + "down": {"uv": [26, 144], "uv_size": [16, -6]} + } + }, + { + "origin": [-7.95649, 0, -3], + "size": [15.91299, 4, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [160, 32], "uv_size": [16, 4]}, + "east": {"uv": [198, 151], "uv_size": [6, 4]}, + "south": {"uv": [160, 36], "uv_size": [16, 4]}, + "west": {"uv": [199, 50], "uv_size": [6, 4]}, + "up": {"uv": [143, 143], "uv_size": [16, 6]}, + "down": {"uv": [144, 6], "uv_size": [16, -6]} + } + }, + { + "origin": [-7.95649, 0, -3], + "size": [15.91299, 4, 6], + "uv": { + "north": {"uv": [160, 40], "uv_size": [16, 4]}, + "east": {"uv": [199, 54], "uv_size": [6, 4]}, + "south": {"uv": [160, 44], "uv_size": [16, 4]}, + "west": {"uv": [155, 200], "uv_size": [6, 4]}, + "up": {"uv": [144, 6], "uv_size": [16, 6]}, + "down": {"uv": [144, 18], "uv_size": [16, -6]} + } + }, + { + "origin": [-7.95649, 0, -3], + "size": [15.91299, 4, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [160, 48], "uv_size": [16, 4]}, + "east": {"uv": [161, 200], "uv_size": [6, 4]}, + "south": {"uv": [160, 52], "uv_size": [16, 4]}, + "west": {"uv": [167, 200], "uv_size": [6, 4]}, + "up": {"uv": [144, 18], "uv_size": [16, 6]}, + "down": {"uv": [144, 30], "uv_size": [16, -6]} + } + }, + { + "origin": [-7.95649, 0, -3], + "size": [15.91299, 4, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, 45], + "uv": { + "north": {"uv": [160, 56], "uv_size": [16, 4]}, + "east": {"uv": [173, 200], "uv_size": [6, 4]}, + "south": {"uv": [143, 160], "uv_size": [16, 4]}, + "west": {"uv": [179, 200], "uv_size": [6, 4]}, + "up": {"uv": [26, 144], "uv_size": [16, 6]}, + "down": {"uv": [144, 36], "uv_size": [16, -6]} + } + }, + { + "origin": [-7.95649, 76, -3], + "size": [15.91299, 4, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, -45], + "uv": { + "north": {"uv": [159, 160], "uv_size": [16, 4]}, + "east": {"uv": [82, 201], "uv_size": [6, 4]}, + "south": {"uv": [143, 164], "uv_size": [16, 4]}, + "west": {"uv": [201, 166], "uv_size": [6, 4]}, + "up": {"uv": [144, 36], "uv_size": [16, 6]}, + "down": {"uv": [144, 48], "uv_size": [16, -6]} + } + }, + { + "origin": [-7.95649, 76, -3], + "size": [15.91299, 4, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [159, 164], "uv_size": [16, 4]}, + "east": {"uv": [201, 170], "uv_size": [6, 4]}, + "south": {"uv": [26, 167], "uv_size": [16, 4]}, + "west": {"uv": [201, 174], "uv_size": [6, 4]}, + "up": {"uv": [144, 48], "uv_size": [16, 6]}, + "down": {"uv": [144, 60], "uv_size": [16, -6]} + } + }, + { + "origin": [-7.95649, 76, -3], + "size": [15.91299, 4, 6], + "uv": { + "north": {"uv": [42, 167], "uv_size": [16, 4]}, + "east": {"uv": [201, 178], "uv_size": [6, 4]}, + "south": {"uv": [58, 167], "uv_size": [16, 4]}, + "west": {"uv": [202, 0], "uv_size": [6, 4]}, + "up": {"uv": [42, 145], "uv_size": [16, 6]}, + "down": {"uv": [58, 151], "uv_size": [16, -6]} + } + }, + { + "origin": [-7.95649, 76, -3], + "size": [15.91299, 4, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [74, 167], "uv_size": [16, 4]}, + "east": {"uv": [202, 4], "uv_size": [6, 4]}, + "south": {"uv": [110, 167], "uv_size": [16, 4]}, + "west": {"uv": [24, 202], "uv_size": [6, 4]}, + "up": {"uv": [74, 145], "uv_size": [16, 6]}, + "down": {"uv": [90, 151], "uv_size": [16, -6]} + } + }, + { + "origin": [-7.95649, 76, -3], + "size": [15.91299, 4, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, 45], + "uv": { + "north": {"uv": [167, 145], "uv_size": [16, 4]}, + "east": {"uv": [30, 202], "uv_size": [6, 4]}, + "south": {"uv": [167, 149], "uv_size": [16, 4]}, + "west": {"uv": [36, 202], "uv_size": [6, 4]}, + "up": {"uv": [106, 145], "uv_size": [16, 6]}, + "down": {"uv": [143, 155], "uv_size": [16, -6]} + } + }, + { + "origin": [36, 32.04351, -3], + "size": [4, 15.91299, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [118, 151], "uv_size": [4, 16]}, + "east": {"uv": [122, 145], "uv_size": [6, 16]}, + "south": {"uv": [168, 0], "uv_size": [4, 16]}, + "west": {"uv": [26, 150], "uv_size": [6, 16]}, + "up": {"uv": [126, 65], "uv_size": [4, 6]}, + "down": {"uv": [126, 77], "uv_size": [4, -6]} + } + }, + { + "origin": [36, 32.04351, -3], + "size": [4, 15.91299, 6], + "uv": { + "north": {"uv": [143, 168], "uv_size": [4, 16]}, + "east": {"uv": [32, 150], "uv_size": [6, 16]}, + "south": {"uv": [147, 168], "uv_size": [4, 16]}, + "west": {"uv": [38, 151], "uv_size": [6, 16]}, + "up": {"uv": [138, 197], "uv_size": [4, 6]}, + "down": {"uv": [88, 207], "uv_size": [4, -6]} + } + }, + { + "origin": [36, 32.04351, -3], + "size": [4, 15.91299, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [151, 168], "uv_size": [4, 16]}, + "east": {"uv": [44, 151], "uv_size": [6, 16]}, + "south": {"uv": [155, 168], "uv_size": [4, 16]}, + "west": {"uv": [50, 151], "uv_size": [6, 16]}, + "up": {"uv": [42, 202], "uv_size": [4, 6]}, + "down": {"uv": [46, 208], "uv_size": [4, -6]} + } + }, + { + "origin": [-40, 32.04351, -3], + "size": [4, 15.91299, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [159, 168], "uv_size": [4, 16]}, + "east": {"uv": [56, 151], "uv_size": [6, 16]}, + "south": {"uv": [163, 168], "uv_size": [4, 16]}, + "west": {"uv": [62, 151], "uv_size": [6, 16]}, + "up": {"uv": [50, 202], "uv_size": [4, 6]}, + "down": {"uv": [54, 208], "uv_size": [4, -6]} + } + }, + { + "origin": [-40, 32.04351, -3], + "size": [4, 15.91299, 6], + "uv": { + "north": {"uv": [167, 168], "uv_size": [4, 16]}, + "east": {"uv": [68, 151], "uv_size": [6, 16]}, + "south": {"uv": [26, 171], "uv_size": [4, 16]}, + "west": {"uv": [74, 151], "uv_size": [6, 16]}, + "up": {"uv": [58, 202], "uv_size": [4, 6]}, + "down": {"uv": [62, 208], "uv_size": [4, -6]} + } + }, + { + "origin": [-40, 32.04351, -3], + "size": [4, 15.91299, 6], + "pivot": [0, 40, -1], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [30, 171], "uv_size": [4, 16]}, + "east": {"uv": [80, 151], "uv_size": [6, 16]}, + "south": {"uv": [34, 171], "uv_size": [4, 16]}, + "west": {"uv": [86, 151], "uv_size": [6, 16]}, + "up": {"uv": [66, 202], "uv_size": [4, 6]}, + "down": {"uv": [70, 208], "uv_size": [4, -6]} + } + } + ] + }, + { + "name": "bone", + "parent": "outter_ring", + "pivot": [0, 27, 0], + "cubes": [ + { + "origin": [10.4, 3.2, -3.8], + "size": [8, 6, 7.5], + "pivot": [18.4, 5.7, -3.8], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [191, 51], "uv_size": [8, 6]}, + "east": {"uv": [155, 194], "uv_size": [8, 6]}, + "south": {"uv": [163, 194], "uv_size": [8, 6]}, + "west": {"uv": [171, 194], "uv_size": [8, 6]}, + "up": {"uv": [160, 8], "uv_size": [8, 8]}, + "down": {"uv": [160, 24], "uv_size": [8, -8]} + } + }, + { + "origin": [-18.4, 3.2, -3.8], + "size": [8, 6, 7.5], + "pivot": [-18.4, 5.7, -3.8], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [199, 14], "uv_size": [8, 6]}, + "east": {"uv": [199, 20], "uv_size": [8, 6]}, + "south": {"uv": [199, 26], "uv_size": [8, 6]}, + "west": {"uv": [199, 32], "uv_size": [8, 6]}, + "up": {"uv": [78, 171], "uv_size": [8, 8]}, + "down": {"uv": [110, 179], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "bone2", + "parent": "bone", + "pivot": [-0.5, 76.8, -3.8], + "cubes": [ + { + "origin": [-4.5, 74.6, -3.8], + "size": [8, 6, 7.5], + "uv": { + "north": {"uv": [118, 73], "uv_size": [8, 6]}, + "east": {"uv": [199, 38], "uv_size": [7, 6]}, + "south": {"uv": [155, 188], "uv_size": [8, 6]}, + "west": {"uv": [199, 44], "uv_size": [7, 6]}, + "up": {"uv": [118, 171], "uv_size": [8, 7]}, + "down": {"uv": [187, 15], "uv_size": [8, -7]} + } + } + ] + }, + { + "name": "bone3", + "parent": "bone", + "pivot": [7.5, 47, -2.1], + "cubes": [ + { + "origin": [19.44583, 63.56781, -3.8], + "size": [8, 6, 7.5], + "pivot": [26.5, 64.6, -3.9], + "rotation": [0, 0, 42.5], + "uv": { + "north": {"uv": [163, 188], "uv_size": [8, 6]}, + "east": {"uv": [171, 188], "uv_size": [8, 6]}, + "south": {"uv": [179, 188], "uv_size": [8, 6]}, + "west": {"uv": [188, 184], "uv_size": [8, 6]}, + "up": {"uv": [118, 65], "uv_size": [8, 8]}, + "down": {"uv": [42, 140], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "bone4", + "parent": "bone", + "pivot": [10.8, 42.2, -2.1], + "cubes": [ + { + "origin": [27, 46.7, -3.8], + "size": [8, 6, 7.5], + "pivot": [35, 46.7, -3.8], + "rotation": [0, 0, 70], + "uv": { + "north": {"uv": [190, 160], "uv_size": [8, 6]}, + "east": {"uv": [187, 190], "uv_size": [8, 6]}, + "south": {"uv": [191, 15], "uv_size": [8, 6]}, + "west": {"uv": [191, 21], "uv_size": [8, 6]}, + "up": {"uv": [110, 151], "uv_size": [8, 8]}, + "down": {"uv": [110, 167], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "bone5", + "parent": "bone", + "pivot": [35.2, 22.1, -2.1], + "cubes": [ + { + "origin": [26.4, 20.7, -3.8], + "size": [8, 6, 7.5], + "pivot": [34.4, 23.2, -3.8], + "rotation": [0, 0, -60], + "uv": { + "north": {"uv": [191, 27], "uv_size": [8, 6]}, + "east": {"uv": [191, 33], "uv_size": [8, 6]}, + "south": {"uv": [191, 39], "uv_size": [8, 6]}, + "west": {"uv": [191, 45], "uv_size": [8, 6]}, + "up": {"uv": [159, 145], "uv_size": [8, 8]}, + "down": {"uv": [160, 8], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "bone6", + "parent": "bone", + "pivot": [-29.9, 22.8, -2.1], + "cubes": [ + { + "origin": [-34.4, 20.7, -3.8], + "size": [8, 6, 7.5], + "pivot": [-34.4, 23.2, -3.8], + "rotation": [0, 0, 60], + "uv": { + "north": {"uv": [8, 198], "uv_size": [8, 6]}, + "east": {"uv": [16, 198], "uv_size": [8, 6]}, + "south": {"uv": [198, 145], "uv_size": [8, 6]}, + "west": {"uv": [198, 160], "uv_size": [8, 6]}, + "up": {"uv": [62, 171], "uv_size": [8, 8]}, + "down": {"uv": [70, 179], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "bone7", + "parent": "bone", + "pivot": [-34.6, 48.8, -2.1], + "cubes": [ + { + "origin": [-35, 46.7, -3.8], + "size": [8, 6, 7.5], + "pivot": [-35, 46.7, -3.8], + "rotation": [0, 0, -70], + "uv": { + "north": {"uv": [187, 196], "uv_size": [8, 6]}, + "east": {"uv": [195, 196], "uv_size": [8, 6]}, + "south": {"uv": [130, 197], "uv_size": [8, 6]}, + "west": {"uv": [0, 198], "uv_size": [8, 6]}, + "up": {"uv": [46, 171], "uv_size": [8, 8]}, + "down": {"uv": [54, 179], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "bone8", + "parent": "bone", + "pivot": [-23.5, 67.1, -2.1], + "cubes": [ + { + "origin": [-28.44583, 66.56781, -3.8], + "size": [8, 6, 7.5], + "pivot": [-23.5, 67.6, -3.9], + "rotation": [0, 0, -42.5], + "uv": { + "north": {"uv": [179, 194], "uv_size": [8, 6]}, + "east": {"uv": [195, 8], "uv_size": [8, 6]}, + "south": {"uv": [195, 190], "uv_size": [8, 6]}, + "west": {"uv": [196, 184], "uv_size": [8, 6]}, + "up": {"uv": [168, 16], "uv_size": [8, 8]}, + "down": {"uv": [38, 179], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "inner_ring", + "parent": "bone9", + "pivot": [0, 40, 0], + "cubes": [ + { + "origin": [-7.35976, 3, -2], + "size": [14.71952, 4, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, -45], + "uv": { + "north": {"uv": [171, 168], "uv_size": [15, 4]}, + "east": {"uv": [48, 140], "uv_size": [4, 4]}, + "south": {"uv": [172, 0], "uv_size": [15, 4]}, + "west": {"uv": [74, 202], "uv_size": [4, 4]}, + "up": {"uv": [172, 4], "uv_size": [15, 4]}, + "down": {"uv": [172, 12], "uv_size": [15, -4]} + } + }, + { + "origin": [-7.35976, 3, -2], + "size": [14.71952, 4, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [172, 12], "uv_size": [15, 4]}, + "east": {"uv": [78, 202], "uv_size": [4, 4]}, + "south": {"uv": [171, 172], "uv_size": [15, 4]}, + "west": {"uv": [110, 202], "uv_size": [4, 4]}, + "up": {"uv": [175, 160], "uv_size": [15, 4]}, + "down": {"uv": [175, 168], "uv_size": [15, -4]} + } + }, + { + "origin": [-7.35976, 3, -2], + "size": [14.71952, 4, 4], + "uv": { + "north": {"uv": [176, 16], "uv_size": [15, 4]}, + "east": {"uv": [114, 202], "uv_size": [4, 4]}, + "south": {"uv": [176, 20], "uv_size": [15, 4]}, + "west": {"uv": [118, 202], "uv_size": [4, 4]}, + "up": {"uv": [176, 24], "uv_size": [15, 4]}, + "down": {"uv": [176, 32], "uv_size": [15, -4]} + } + }, + { + "origin": [-7.35976, 3, -2], + "size": [14.71952, 4, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [176, 32], "uv_size": [15, 4]}, + "east": {"uv": [122, 202], "uv_size": [4, 4]}, + "south": {"uv": [176, 36], "uv_size": [15, 4]}, + "west": {"uv": [185, 202], "uv_size": [4, 4]}, + "up": {"uv": [176, 40], "uv_size": [15, 4]}, + "down": {"uv": [176, 48], "uv_size": [15, -4]} + } + }, + { + "origin": [-7.35976, 3, -2], + "size": [14.71952, 4, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, 45], + "uv": { + "north": {"uv": [176, 48], "uv_size": [15, 4]}, + "east": {"uv": [189, 202], "uv_size": [4, 4]}, + "south": {"uv": [176, 52], "uv_size": [15, 4]}, + "west": {"uv": [193, 202], "uv_size": [4, 4]}, + "up": {"uv": [176, 56], "uv_size": [15, 4]}, + "down": {"uv": [171, 180], "uv_size": [15, -4]} + } + }, + { + "origin": [-7.35976, 73, -2], + "size": [14.71952, 4, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, -45], + "uv": { + "north": {"uv": [38, 179], "uv_size": [15, 4]}, + "east": {"uv": [197, 202], "uv_size": [4, 4]}, + "south": {"uv": [53, 179], "uv_size": [15, 4]}, + "west": {"uv": [201, 202], "uv_size": [4, 4]}, + "up": {"uv": [68, 179], "uv_size": [15, 4]}, + "down": {"uv": [110, 183], "uv_size": [15, -4]} + } + }, + { + "origin": [-7.35976, 73, -2], + "size": [14.71952, 4, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [171, 180], "uv_size": [15, 4]}, + "east": {"uv": [203, 8], "uv_size": [4, 4]}, + "south": {"uv": [38, 183], "uv_size": [15, 4]}, + "west": {"uv": [130, 203], "uv_size": [4, 4]}, + "up": {"uv": [53, 183], "uv_size": [15, 4]}, + "down": {"uv": [68, 187], "uv_size": [15, -4]} + } + }, + { + "origin": [-7.35976, 73, -2], + "size": [14.71952, 4, 4], + "uv": { + "north": {"uv": [110, 183], "uv_size": [15, 4]}, + "east": {"uv": [134, 203], "uv_size": [4, 4]}, + "south": {"uv": [183, 145], "uv_size": [15, 4]}, + "west": {"uv": [138, 203], "uv_size": [4, 4]}, + "up": {"uv": [183, 149], "uv_size": [15, 4]}, + "down": {"uv": [143, 188], "uv_size": [15, -4]} + } + }, + { + "origin": [-7.35976, 73, -2], + "size": [14.71952, 4, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [158, 184], "uv_size": [15, 4]}, + "east": {"uv": [142, 203], "uv_size": [4, 4]}, + "south": {"uv": [173, 184], "uv_size": [15, 4]}, + "west": {"uv": [146, 203], "uv_size": [4, 4]}, + "up": {"uv": [186, 168], "uv_size": [15, 4]}, + "down": {"uv": [186, 176], "uv_size": [15, -4]} + } + }, + { + "origin": [-7.35976, 73, -2], + "size": [14.71952, 4, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, 45], + "uv": { + "north": {"uv": [186, 176], "uv_size": [15, 4]}, + "east": {"uv": [150, 203], "uv_size": [4, 4]}, + "south": {"uv": [186, 180], "uv_size": [15, 4]}, + "west": {"uv": [203, 190], "uv_size": [4, 4]}, + "up": {"uv": [187, 0], "uv_size": [15, 4]}, + "down": {"uv": [187, 8], "uv_size": [15, -4]} + } + }, + { + "origin": [33, 32.64024, -2], + "size": [4, 14.71952, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [86, 171], "uv_size": [4, 15]}, + "east": {"uv": [83, 186], "uv_size": [4, 15]}, + "south": {"uv": [87, 186], "uv_size": [4, 15]}, + "west": {"uv": [26, 187], "uv_size": [4, 15]}, + "up": {"uv": [203, 194], "uv_size": [4, 4]}, + "down": {"uv": [203, 202], "uv_size": [4, -4]} + } + }, + { + "origin": [33, 32.64024, -2], + "size": [4, 14.71952, 4], + "uv": { + "north": {"uv": [30, 187], "uv_size": [4, 15]}, + "east": {"uv": [34, 187], "uv_size": [4, 15]}, + "south": {"uv": [38, 187], "uv_size": [4, 15]}, + "west": {"uv": [42, 187], "uv_size": [4, 15]}, + "up": {"uv": [0, 204], "uv_size": [4, 4]}, + "down": {"uv": [4, 208], "uv_size": [4, -4]} + } + }, + { + "origin": [33, 32.64024, -2], + "size": [4, 14.71952, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [46, 187], "uv_size": [4, 15]}, + "east": {"uv": [50, 187], "uv_size": [4, 15]}, + "south": {"uv": [54, 187], "uv_size": [4, 15]}, + "west": {"uv": [58, 187], "uv_size": [4, 15]}, + "up": {"uv": [8, 204], "uv_size": [4, 4]}, + "down": {"uv": [12, 208], "uv_size": [4, -4]} + } + }, + { + "origin": [-37, 32.64024, -2], + "size": [4, 14.71952, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [62, 187], "uv_size": [4, 15]}, + "east": {"uv": [66, 187], "uv_size": [4, 15]}, + "south": {"uv": [70, 187], "uv_size": [4, 15]}, + "west": {"uv": [74, 187], "uv_size": [4, 15]}, + "up": {"uv": [16, 204], "uv_size": [4, 4]}, + "down": {"uv": [20, 208], "uv_size": [4, -4]} + } + }, + { + "origin": [-37, 32.64024, -2], + "size": [4, 14.71952, 4], + "uv": { + "north": {"uv": [78, 187], "uv_size": [4, 15]}, + "east": {"uv": [110, 187], "uv_size": [4, 15]}, + "south": {"uv": [114, 187], "uv_size": [4, 15]}, + "west": {"uv": [118, 187], "uv_size": [4, 15]}, + "up": {"uv": [204, 151], "uv_size": [4, 4]}, + "down": {"uv": [154, 208], "uv_size": [4, -4]} + } + }, + { + "origin": [-37, 32.64024, -2], + "size": [4, 14.71952, 4], + "pivot": [0, 40, 0], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [122, 187], "uv_size": [4, 15]}, + "east": {"uv": [143, 188], "uv_size": [4, 15]}, + "south": {"uv": [147, 188], "uv_size": [4, 15]}, + "west": {"uv": [151, 188], "uv_size": [4, 15]}, + "up": {"uv": [158, 204], "uv_size": [4, 4]}, + "down": {"uv": [162, 208], "uv_size": [4, -4]} + } + } + ] + }, + { + "name": "portal", + "parent": "bone9", + "pivot": [0, 40, -0.7], + "cubes": [ + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, -45], + "uv": { + "north": {"uv": [52, 79], "uv_size": [13, 66]}, + "east": {"uv": [100, 151], "uv_size": [1, 66]}, + "south": {"uv": [65, 79], "uv_size": [13, 66]}, + "west": {"uv": [101, 151], "uv_size": [1, 66]}, + "up": {"uv": [191, 59], "uv_size": [13, 1]}, + "down": {"uv": [201, 183], "uv_size": [13, -1]} + } + }, + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [78, 79], "uv_size": [13, 66]}, + "east": {"uv": [102, 151], "uv_size": [1, 66]}, + "south": {"uv": [91, 79], "uv_size": [13, 66]}, + "west": {"uv": [103, 151], "uv_size": [1, 66]}, + "up": {"uv": [201, 183], "uv_size": [13, 1]}, + "down": {"uv": [203, 13], "uv_size": [13, -1]} + } + }, + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "uv": { + "north": {"uv": [104, 79], "uv_size": [13, 66]}, + "east": {"uv": [104, 151], "uv_size": [1, 66]}, + "south": {"uv": [117, 79], "uv_size": [13, 66]}, + "west": {"uv": [105, 151], "uv_size": [1, 66]}, + "up": {"uv": [203, 13], "uv_size": [13, 1]}, + "down": {"uv": [204, 59], "uv_size": [13, -1]} + } + }, + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [130, 65], "uv_size": [13, 66]}, + "east": {"uv": [106, 151], "uv_size": [1, 66]}, + "south": {"uv": [130, 131], "uv_size": [13, 66]}, + "west": {"uv": [107, 151], "uv_size": [1, 66]}, + "up": {"uv": [204, 59], "uv_size": [13, 1]}, + "down": {"uv": [166, 205], "uv_size": [13, -1]} + } + }, + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, 45], + "uv": { + "north": {"uv": [0, 132], "uv_size": [13, 66]}, + "east": {"uv": [108, 151], "uv_size": [1, 66]}, + "south": {"uv": [13, 132], "uv_size": [13, 66]}, + "west": {"uv": [109, 151], "uv_size": [1, 66]}, + "up": {"uv": [204, 184], "uv_size": [13, 1]}, + "down": {"uv": [204, 186], "uv_size": [13, -1]} + } + }, + { + "origin": [-33, 33.43589, -0.7], + "size": [66, 13.12822, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [143, 65], "uv_size": [66, 13]}, + "east": {"uv": [91, 180], "uv_size": [1, 13]}, + "south": {"uv": [143, 78], "uv_size": [66, 13]}, + "west": {"uv": [82, 187], "uv_size": [1, 13]}, + "up": {"uv": [143, 156], "uv_size": [66, 1]}, + "down": {"uv": [143, 158], "uv_size": [66, -1]} + } + }, + { + "origin": [-33, 33.43589, -0.7], + "size": [66, 13.12822, 1], + "uv": { + "north": {"uv": [143, 91], "uv_size": [66, 13]}, + "east": {"uv": [126, 191], "uv_size": [1, 13]}, + "south": {"uv": [143, 104], "uv_size": [66, 13]}, + "west": {"uv": [127, 191], "uv_size": [1, 13]}, + "up": {"uv": [143, 158], "uv_size": [66, 1]}, + "down": {"uv": [143, 160], "uv_size": [66, -1]} + } + }, + { + "origin": [-33, 33.43589, -0.7], + "size": [66, 13.12822, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [143, 117], "uv_size": [66, 13]}, + "east": {"uv": [126, 204], "uv_size": [1, 13]}, + "south": {"uv": [143, 130], "uv_size": [66, 13]}, + "west": {"uv": [127, 204], "uv_size": [1, 13]}, + "up": {"uv": [159, 143], "uv_size": [66, 1]}, + "down": {"uv": [159, 145], "uv_size": [66, -1]} + } + } + ] + }, + { + "name": "portal2", + "parent": "bone9", + "pivot": [0, 40, -0.7], + "cubes": [ + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, -45], + "uv": { + "north": {"uv": [0, 0], "uv_size": [13, 66]}, + "east": {"uv": [128, 145], "uv_size": [1, 66]}, + "south": {"uv": [13, 0], "uv_size": [13, 66]}, + "west": {"uv": [129, 145], "uv_size": [1, 66]}, + "up": {"uv": [78, 65], "uv_size": [13, 1]}, + "down": {"uv": [91, 66], "uv_size": [13, -1]} + } + }, + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [26, 0], "uv_size": [13, 66]}, + "east": {"uv": [92, 151], "uv_size": [1, 66]}, + "south": {"uv": [39, 0], "uv_size": [13, 66]}, + "west": {"uv": [93, 151], "uv_size": [1, 66]}, + "up": {"uv": [104, 65], "uv_size": [13, 1]}, + "down": {"uv": [159, 154], "uv_size": [13, -1]} + } + }, + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "uv": { + "north": {"uv": [52, 0], "uv_size": [13, 66]}, + "east": {"uv": [94, 151], "uv_size": [1, 66]}, + "south": {"uv": [65, 0], "uv_size": [13, 66]}, + "west": {"uv": [95, 151], "uv_size": [1, 66]}, + "up": {"uv": [159, 154], "uv_size": [13, 1]}, + "down": {"uv": [172, 154], "uv_size": [13, -1]} + } + }, + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [0, 66], "uv_size": [13, 66]}, + "east": {"uv": [96, 151], "uv_size": [1, 66]}, + "south": {"uv": [13, 66], "uv_size": [13, 66]}, + "west": {"uv": [97, 151], "uv_size": [1, 66]}, + "up": {"uv": [172, 154], "uv_size": [13, 1]}, + "down": {"uv": [185, 154], "uv_size": [13, -1]} + } + }, + { + "origin": [-6.56411, 7, -0.7], + "size": [13.12822, 66, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, 45], + "uv": { + "north": {"uv": [26, 66], "uv_size": [13, 66]}, + "east": {"uv": [98, 151], "uv_size": [1, 66]}, + "south": {"uv": [39, 66], "uv_size": [13, 66]}, + "west": {"uv": [99, 151], "uv_size": [1, 66]}, + "up": {"uv": [185, 154], "uv_size": [13, 1]}, + "down": {"uv": [191, 59], "uv_size": [13, -1]} + } + }, + { + "origin": [-33, 33.43589, -0.7], + "size": [66, 13.12822, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [52, 66], "uv_size": [66, 13]}, + "east": {"uv": [126, 165], "uv_size": [1, 13]}, + "south": {"uv": [78, 0], "uv_size": [66, 13]}, + "west": {"uv": [127, 165], "uv_size": [1, 13]}, + "up": {"uv": [144, 60], "uv_size": [66, 1]}, + "down": {"uv": [144, 62], "uv_size": [66, -1]} + } + }, + { + "origin": [-33, 33.43589, -0.7], + "size": [66, 13.12822, 1], + "uv": { + "north": {"uv": [78, 13], "uv_size": [66, 13]}, + "east": {"uv": [90, 167], "uv_size": [1, 13]}, + "south": {"uv": [78, 26], "uv_size": [66, 13]}, + "west": {"uv": [91, 167], "uv_size": [1, 13]}, + "up": {"uv": [144, 62], "uv_size": [66, 1]}, + "down": {"uv": [144, 64], "uv_size": [66, -1]} + } + }, + { + "origin": [-33, 33.43589, -0.7], + "size": [66, 13.12822, 1], + "pivot": [0, 40, 1.3], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [78, 39], "uv_size": [66, 13]}, + "east": {"uv": [126, 178], "uv_size": [1, 13]}, + "south": {"uv": [78, 52], "uv_size": [66, 13]}, + "west": {"uv": [127, 178], "uv_size": [1, 13]}, + "up": {"uv": [144, 64], "uv_size": [66, 1]}, + "down": {"uv": [143, 156], "uv_size": [66, -1]} + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/geo/entity/marauder.geo.json b/common/src/main/resources/assets/azurelib/geo/entity/marauder.geo.json new file mode 100644 index 000000000..2cd28b0b0 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/geo/entity/marauder.geo.json @@ -0,0 +1,4168 @@ +{ + "format_version": "1.21.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.unknown", + "texture_width": 512, + "texture_height": 128, + "visible_bounds_width": 14, + "visible_bounds_height": 8, + "visible_bounds_offset": [0, 2, 0] + }, + "bones": [ + { + "name": "dreadnought", + "pivot": [0, 0, 0] + }, + { + "name": "body2", + "parent": "dreadnought", + "pivot": [0, 21, 0] + }, + { + "name": "torso", + "parent": "body2", + "pivot": [0, 21, 0], + "cubes": [ + { + "origin": [-4, 19, -2], + "size": [8, 7, 4], + "uv": { + "north": {"uv": [411, 18], "uv_size": [8, 7]}, + "east": {"uv": [384, 55], "uv_size": [4, 7]}, + "south": {"uv": [411, 25], "uv_size": [8, 7]}, + "west": {"uv": [423, 55], "uv_size": [4, 7]}, + "up": {"uv": [438, 27], "uv_size": [8, 4]}, + "down": {"uv": [439, 49], "uv_size": [8, -4]} + } + }, + { + "origin": [-4, 22.25, -2], + "size": [8, 2, 4], + "inflate": 0.25, + "uv": { + "north": {"uv": [451, 33], "uv_size": [8, 2]}, + "east": {"uv": [456, 45], "uv_size": [4, 2]}, + "south": {"uv": [451, 43], "uv_size": [8, 2]}, + "west": {"uv": [456, 47], "uv_size": [4, 2]} + } + } + ] + }, + { + "name": "upper_torso", + "parent": "torso", + "pivot": [0, 25, 0], + "cubes": [ + { + "origin": [-5.5, 25, -2.5], + "size": [11, 9, 5], + "uv": { + "north": {"uv": [403, 0], "uv_size": [11, 9]}, + "east": {"uv": [423, 35], "uv_size": [5, 9]}, + "south": {"uv": [403, 9], "uv_size": [11, 9]}, + "west": {"uv": [425, 0], "uv_size": [5, 9]}, + "up": {"uv": [414, 0], "uv_size": [11, 5]}, + "down": {"uv": [414, 10], "uv_size": [11, -5]} + } + }, + { + "origin": [-2, 34, -2], + "size": [4, 2, 4], + "uv": { + "north": {"uv": [430, 7], "uv_size": [4, 2]}, + "east": {"uv": [446, 29], "uv_size": [4, 2]}, + "south": {"uv": [457, 25], "uv_size": [4, 2]}, + "west": {"uv": [457, 27], "uv_size": [4, 2]}, + "up": {"uv": [449, 9], "uv_size": [4, 4]}, + "down": {"uv": [395, 69], "uv_size": [4, -4]} + } + }, + { + "origin": [0, 27, -2.75], + "size": [6, 7, 5.5], + "inflate": 0.01, + "pivot": [3, 30, 0], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [428, 9], "uv_size": [6, 7]}, + "east": {"uv": [430, 0], "uv_size": [6, 7]}, + "south": {"uv": [427, 46], "uv_size": [6, 7]}, + "west": {"uv": [431, 16], "uv_size": [6, 7]}, + "up": {"uv": [433, 41], "uv_size": [6, 6]}, + "down": {"uv": [433, 53], "uv_size": [6, -6]} + } + }, + { + "origin": [1.75, 29, -2.85], + "size": [3, 3, 0], + "inflate": 0.01, + "uv": { + "north": {"uv": [387, 79], "uv_size": [-3, 3]}, + "east": {"uv": [430, 0], "uv_size": [6, 7]}, + "south": {"uv": [384, 79], "uv_size": [3, 3]}, + "west": {"uv": [431, 16], "uv_size": [6, 7]}, + "up": {"uv": [433, 41], "uv_size": [6, 6]}, + "down": {"uv": [433, 53], "uv_size": [6, -6]} + } + }, + { + "origin": [-6.77164, 29.14805, -2.75], + "size": [7, 2, 0], + "pivot": [0.22836, 31.14805, -2.75], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [438, 29], "uv_size": [-7, 2]}, + "east": {"uv": [384, 0], "uv_size": [0, 2]}, + "south": {"uv": [431, 29], "uv_size": [7, 2]}, + "west": {"uv": [384, 0], "uv_size": [0, 2]}, + "up": {"uv": [384, 0], "uv_size": [7, 0]}, + "down": {"uv": [384, 0], "uv_size": [7, 0]} + } + }, + { + "origin": [-3.39104, 29.23463, -3], + "size": [2, 4, 6], + "pivot": [-4.39104, 29.23463, -2.75], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [393, 72], "uv_size": [2, 4]}, + "east": {"uv": [395, 61], "uv_size": [6, 4]}, + "south": {"uv": [410, 72], "uv_size": [2, 4]}, + "west": {"uv": [445, 31], "uv_size": [6, 4]}, + "up": {"uv": [436, 0], "uv_size": [2, 6]}, + "down": {"uv": [410, 68], "uv_size": [2, -6]} + } + }, + { + "origin": [-3.89104, 31.73463, -2.75], + "size": [3, 3, 5.5], + "pivot": [-4.39104, 29.23463, -2.75], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [455, 49], "uv_size": [3, 3]}, + "east": {"uv": [447, 54], "uv_size": [6, 3]}, + "south": {"uv": [456, 13], "uv_size": [3, 3]}, + "west": {"uv": [448, 0], "uv_size": [6, 3]}, + "up": {"uv": [396, 50], "uv_size": [3, 6]}, + "down": {"uv": [449, 9], "uv_size": [3, -6]} + } + } + ] + }, + { + "name": "h_head_furious", + "parent": "upper_torso", + "pivot": [0, 35, 0], + "cubes": [ + { + "origin": [-3.5, 34.5, -4], + "size": [7, 4, 4], + "inflate": 0.25, + "pivot": [0, 37.5, -2], + "rotation": [22.5, 0, 0], + "uv": { + "north": {"uv": [442, 5], "uv_size": [7, 4]}, + "east": {"uv": [449, 13], "uv_size": [4, 4]}, + "south": {"uv": [442, 9], "uv_size": [7, 4]}, + "west": {"uv": [449, 17], "uv_size": [4, 4]}, + "up": {"uv": [442, 13], "uv_size": [7, 4]}, + "down": {"uv": [442, 21], "uv_size": [7, -4]} + } + }, + { + "origin": [-3.5, 35, -3.5], + "size": [7, 7, 7], + "uv": { + "north": {"uv": [414, 10], "uv_size": [7, 7]}, + "east": {"uv": [389, 34], "uv_size": [7, 7]}, + "south": {"uv": [396, 34], "uv_size": [7, 7]}, + "west": {"uv": [419, 17], "uv_size": [7, 7]}, + "up": {"uv": [419, 24], "uv_size": [7, 7]}, + "down": {"uv": [421, 17], "uv_size": [7, -7]} + } + }, + { + "origin": [-0.1, 39.75, -5.25], + "size": [0.1, 2, 2], + "pivot": [0, 40, -3.5], + "rotation": [-45, 0, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [0, 2]}, + "east": {"uv": [446, 72], "uv_size": [-2, 2]}, + "south": {"uv": [384, 0], "uv_size": [0, 2]}, + "west": {"uv": [444, 72], "uv_size": [2, 2]}, + "up": {"uv": [384, 0], "uv_size": [0, 2]}, + "down": {"uv": [384, 2], "uv_size": [0, -2]} + } + }, + { + "origin": [-0.1, 42, -2.5], + "size": [0.1, 2, 2], + "pivot": [0, 42, -2.5], + "rotation": [-45, 0, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [0, 2]}, + "east": {"uv": [448, 72], "uv_size": [-2, 2]}, + "south": {"uv": [384, 0], "uv_size": [0, 2]}, + "west": {"uv": [446, 72], "uv_size": [2, 2]}, + "up": {"uv": [384, 0], "uv_size": [0, 2]}, + "down": {"uv": [384, 2], "uv_size": [0, -2]} + } + }, + { + "origin": [3.5, 38.5, -7.5], + "size": [3, 0.1, 11], + "pivot": [3.5, 38.501, 0.5], + "rotation": [22.5, 0, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [3, 0]}, + "east": {"uv": [384, 0], "uv_size": [9, 0]}, + "south": {"uv": [384, 0], "uv_size": [3, 0]}, + "west": {"uv": [384, 0], "uv_size": [9, 0]}, + "up": {"uv": [509, 0], "uv_size": [3, 11]}, + "down": {"uv": [509, 11], "uv_size": [3, -11]} + } + }, + { + "origin": [-6.5, 38.5, -7.5], + "size": [3, 0.1, 11], + "pivot": [-3.5, 38.501, 0.5], + "rotation": [22.5, 0, 0], + "uv": { + "north": {"uv": [387, 0], "uv_size": [-3, 0]}, + "east": {"uv": [393, 0], "uv_size": [-9, 0]}, + "south": {"uv": [387, 0], "uv_size": [-3, 0]}, + "west": {"uv": [393, 0], "uv_size": [-9, 0]}, + "up": {"uv": [512, 0], "uv_size": [-3, 11]}, + "down": {"uv": [512, 11], "uv_size": [-3, -11]} + } + }, + { + "origin": [0, 34.25, -5.32843], + "size": [4, 4, 3], + "pivot": [0, 38.25, -5.32843], + "rotation": [0, -22.5, 0], + "uv": { + "north": {"uv": [423, 65], "uv_size": [4, 4]}, + "east": {"uv": [453, 25], "uv_size": [3, 4]}, + "south": {"uv": [449, 57], "uv_size": [4, 4]}, + "west": {"uv": [451, 69], "uv_size": [3, 4]}, + "up": {"uv": [454, 0], "uv_size": [4, 3]}, + "down": {"uv": [454, 38], "uv_size": [4, -3]} + } + }, + { + "origin": [-4, 34.25, -5.32843], + "size": [4, 4, 3], + "pivot": [0, 38.25, -5.32843], + "rotation": [0, 22.5, 0], + "uv": { + "north": {"uv": [427, 65], "uv_size": [-4, 4]}, + "east": {"uv": [454, 69], "uv_size": [-3, 4]}, + "south": {"uv": [453, 57], "uv_size": [-4, 4]}, + "west": {"uv": [456, 25], "uv_size": [-3, 4]}, + "up": {"uv": [458, 0], "uv_size": [-4, 3]}, + "down": {"uv": [458, 38], "uv_size": [-4, -3]} + } + } + ] + }, + { + "name": "eyes_attack", + "parent": "h_head_furious", + "pivot": [-2.9, 38.25, -3.6], + "cubes": [ + { + "origin": [-3.4, 38.25, -3.6], + "size": [2.5, 2, 0.1], + "pivot": [-2.9, 38.25, -3.6], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [454, 12], "uv_size": [-1, 2]}, + "east": {"uv": [426, 17], "uv_size": [-7, 7]}, + "south": {"uv": [454, 12], "uv_size": [-1, 2]}, + "west": {"uv": [396, 34], "uv_size": [-7, 7]}, + "up": {"uv": [426, 24], "uv_size": [-7, 7]}, + "down": {"uv": [428, 17], "uv_size": [-7, -7]} + } + }, + { + "origin": [0.9, 38.25, -3.6], + "size": [2.5, 2, 0.1], + "pivot": [2.9, 38.25, -3.6], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [453, 12], "uv_size": [1, 2]}, + "east": {"uv": [389, 34], "uv_size": [7, 7]}, + "south": {"uv": [453, 12], "uv_size": [1, 2]}, + "west": {"uv": [419, 17], "uv_size": [7, 7]}, + "up": {"uv": [419, 24], "uv_size": [7, 7]}, + "down": {"uv": [421, 17], "uv_size": [7, -7]} + } + }, + { + "origin": [1.4, 38.25, -3.8], + "size": [1, 1, 0.1], + "uv": { + "north": {"uv": [451, 126], "uv_size": [1, 2]}, + "east": {"uv": [389, 34], "uv_size": [7, 7]}, + "south": {"uv": [393, 15], "uv_size": [1, 2]}, + "west": {"uv": [419, 17], "uv_size": [7, 7]}, + "up": {"uv": [419, 24], "uv_size": [7, 7]}, + "down": {"uv": [421, 17], "uv_size": [7, -7]} + } + }, + { + "origin": [-2.4, 38.25, -3.8], + "size": [1, 1, 0.1], + "uv": { + "north": {"uv": [452, 126], "uv_size": [-1, 2]}, + "east": {"uv": [426, 17], "uv_size": [-7, 7]}, + "south": {"uv": [394, 15], "uv_size": [-1, 2]}, + "west": {"uv": [396, 34], "uv_size": [-7, 7]}, + "up": {"uv": [426, 24], "uv_size": [-7, 7]}, + "down": {"uv": [428, 17], "uv_size": [-7, -7]} + } + } + ] + }, + { + "name": "eyes_normal", + "parent": "h_head_furious", + "pivot": [-2.9, 38.25, -3.6], + "cubes": [ + { + "origin": [-3.4, 38.25, -3.6], + "size": [2.5, 2, 0.1], + "pivot": [-2.9, 38.25, -3.6], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [456, 13], "uv_size": [-2, 2]}, + "east": {"uv": [426, 17], "uv_size": [-7, 7]}, + "south": {"uv": [456, 13], "uv_size": [-2, 2]}, + "west": {"uv": [396, 34], "uv_size": [-7, 7]}, + "up": {"uv": [426, 24], "uv_size": [-7, 7]}, + "down": {"uv": [428, 17], "uv_size": [-7, -7]} + } + }, + { + "origin": [0.9, 38.25, -3.6], + "size": [2.5, 2, 0.1], + "pivot": [2.9, 38.25, -3.6], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [456, 13], "uv_size": [-2, 2]}, + "east": {"uv": [389, 34], "uv_size": [7, 7]}, + "south": {"uv": [456, 13], "uv_size": [-2, 2]}, + "west": {"uv": [419, 17], "uv_size": [7, 7]}, + "up": {"uv": [419, 24], "uv_size": [7, 7]}, + "down": {"uv": [421, 17], "uv_size": [7, -7]} + } + }, + { + "origin": [-2.4, 38.25, -3.8], + "size": [1, 1, 0.1], + "uv": { + "north": {"uv": [454, 12], "uv_size": [-1, 2]}, + "east": {"uv": [454, 12], "uv_size": [-1, 2]}, + "south": {"uv": [454, 12], "uv_size": [-1, 2]}, + "west": {"uv": [454, 12], "uv_size": [-1, 2]}, + "up": {"uv": [453, 14], "uv_size": [1, -2]}, + "down": {"uv": [428, 17], "uv_size": [-7, -7]} + } + }, + { + "origin": [1.4, 38.25, -3.8], + "size": [1, 1, 0.1], + "uv": { + "north": {"uv": [454, 12], "uv_size": [-1, 2]}, + "east": {"uv": [454, 12], "uv_size": [-1, 2]}, + "south": {"uv": [454, 12], "uv_size": [-1, 2]}, + "west": {"uv": [454, 12], "uv_size": [-1, 2]}, + "up": {"uv": [453, 14], "uv_size": [1, -2]}, + "down": {"uv": [421, 17], "uv_size": [7, -7]} + } + } + ] + }, + { + "name": "h_left_horn_furious", + "parent": "h_head_furious", + "pivot": [0, 40.5, 0], + "rotation": [60, -15, -15], + "cubes": [ + { + "origin": [1.5, 41, -3], + "size": [3, 5, 3], + "pivot": [2.5, 42.5, -1.5], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [427, 65], "uv_size": [3, 5]}, + "east": {"uv": [388, 68], "uv_size": [3, 5]}, + "south": {"uv": [453, 7], "uv_size": [3, 5]}, + "west": {"uv": [395, 69], "uv_size": [3, 5]}, + "up": {"uv": [398, 72], "uv_size": [3, 3]}, + "down": {"uv": [401, 75], "uv_size": [3, -3]} + } + }, + { + "origin": [3.18715, 44.96821, -2.5], + "size": [2, 5, 2], + "pivot": [5.68715, 44.96821, -3], + "rotation": [-22.5, 0, 0], + "uv": { + "north": {"uv": [455, 38], "uv_size": [2, 5]}, + "east": {"uv": [440, 71], "uv_size": [2, 5]}, + "south": {"uv": [442, 71], "uv_size": [2, 5]}, + "west": {"uv": [384, 72], "uv_size": [2, 5]}, + "up": {"uv": [456, 64], "uv_size": [2, 2]}, + "down": {"uv": [456, 68], "uv_size": [2, -2]} + } + }, + { + "origin": [4.08715, 47.39627, -0.62464], + "size": [0.1, 2, 5], + "pivot": [4.18715, 49.39627, -0.62464], + "rotation": [22.5, 0, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [0, 5]}, + "east": {"uv": [394, 79], "uv_size": [-5, -2], "uv_rotation": 90}, + "south": {"uv": [384, 0], "uv_size": [0, 5]}, + "west": {"uv": [389, 79], "uv_size": [5, -2], "uv_rotation": 90}, + "up": {"uv": [384, 0], "uv_size": [0, 2]}, + "down": {"uv": [384, 2], "uv_size": [0, -2]} + } + } + ] + }, + { + "name": "h_right_horn_furious", + "parent": "h_head_furious", + "pivot": [0, 40.5, 0], + "rotation": [60, 15, 15], + "cubes": [ + { + "origin": [-4.5, 41, -3], + "size": [3, 5, 3], + "pivot": [-2.5, 42.5, -1.5], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [430, 65], "uv_size": [-3, 5]}, + "east": {"uv": [398, 69], "uv_size": [-3, 5]}, + "south": {"uv": [456, 7], "uv_size": [-3, 5]}, + "west": {"uv": [391, 68], "uv_size": [-3, 5]}, + "up": {"uv": [401, 72], "uv_size": [-3, 3]}, + "down": {"uv": [404, 75], "uv_size": [-3, -3]} + } + }, + { + "origin": [-5.18715, 44.96821, -2.5], + "size": [2, 5, 2], + "pivot": [-5.68715, 44.96821, -3], + "rotation": [-22.5, 0, 0], + "uv": { + "north": {"uv": [391, 123], "uv_size": [2, 5]}, + "east": {"uv": [393, 123], "uv_size": [2, 5]}, + "south": {"uv": [389, 123], "uv_size": [2, 5]}, + "west": {"uv": [395, 123], "uv_size": [2, 5]}, + "up": {"uv": [391, 123], "uv_size": [-2, -2]}, + "down": {"uv": [393, 123], "uv_size": [-2, -2]} + } + }, + { + "origin": [-4.28715, 47.39627, -0.62464], + "size": [0.1, 2, 5], + "pivot": [-4.18715, 49.39627, -0.62464], + "rotation": [22.5, 0, 0], + "uv": { + "north": {"uv": [395, 85], "uv_size": [-5, -2], "uv_rotation": 90}, + "east": {"uv": [394, 79], "uv_size": [-5, -2], "uv_rotation": 90}, + "south": {"uv": [395, 85], "uv_size": [-5, -2], "uv_rotation": 90}, + "west": {"uv": [389, 79], "uv_size": [5, -2], "uv_rotation": 90}, + "up": {"uv": [390, 83], "uv_size": [5, 2], "uv_rotation": 90}, + "down": {"uv": [390, 83], "uv_size": [5, 2], "uv_rotation": 90} + } + } + ] + }, + { + "name": "left_arm", + "parent": "upper_torso", + "pivot": [6, 32.5, 0], + "rotation": [0, 0, -22.5], + "cubes": [ + { + "origin": [4, 26, -2], + "size": [5, 9, 4], + "uv": { + "north": {"uv": [389, 41], "uv_size": [5, 9]}, + "east": {"uv": [388, 50], "uv_size": [4, 9]}, + "south": {"uv": [394, 41], "uv_size": [5, 9]}, + "west": {"uv": [434, 7], "uv_size": [4, 9]}, + "up": {"uv": [446, 41], "uv_size": [5, 4]}, + "down": {"uv": [436, 66], "uv_size": [5, -4]} + } + }, + { + "origin": [7, 30, -2], + "size": [4, 0.1, 4], + "pivot": [9, 29, 0], + "rotation": [0, -45, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [4, 0]}, + "east": {"uv": [384, 0], "uv_size": [4, 0]}, + "south": {"uv": [384, 0], "uv_size": [4, 0]}, + "west": {"uv": [384, 0], "uv_size": [4, 0]}, + "up": {"uv": [449, 61], "uv_size": [4, 4]}, + "down": {"uv": [449, 65], "uv_size": [4, -4]} + } + }, + { + "origin": [6.75, 29, -1.75], + "size": [4, 0.1, 4], + "pivot": [9, 29, 0], + "rotation": [0, -45, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [4, 0]}, + "east": {"uv": [384, 0], "uv_size": [4, 0]}, + "south": {"uv": [384, 0], "uv_size": [4, 0]}, + "west": {"uv": [384, 0], "uv_size": [4, 0]}, + "up": {"uv": [449, 61], "uv_size": [4, 4]}, + "down": {"uv": [449, 65], "uv_size": [4, -4]} + } + }, + { + "origin": [6.5, 28, -1.5], + "size": [4, 0.1, 4], + "pivot": [9, 29, 0], + "rotation": [0, -45, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [4, 0]}, + "east": {"uv": [384, 0], "uv_size": [4, 0]}, + "south": {"uv": [384, 0], "uv_size": [4, 0]}, + "west": {"uv": [384, 0], "uv_size": [4, 0]}, + "up": {"uv": [449, 61], "uv_size": [4, 4]}, + "down": {"uv": [449, 65], "uv_size": [4, -4]} + } + }, + { + "origin": [6, 31, -3], + "size": [4, 5, 6], + "uv": { + "north": {"uv": [441, 62], "uv_size": [4, 5]}, + "east": {"uv": [396, 56], "uv_size": [6, 5]}, + "south": {"uv": [401, 63], "uv_size": [4, 5]}, + "west": {"uv": [406, 57], "uv_size": [6, 5]}, + "up": {"uv": [445, 57], "uv_size": [4, 6]}, + "down": {"uv": [384, 68], "uv_size": [4, -6]} + } + }, + { + "origin": [10, 31, -2.5], + "size": [3, 0.1, 5], + "pivot": [10, 31, 0], + "rotation": [0, 0, -45], + "uv": { + "north": {"uv": [384, 0], "uv_size": [3, 0]}, + "east": {"uv": [384, 0], "uv_size": [5, 0]}, + "south": {"uv": [384, 0], "uv_size": [3, 0]}, + "west": {"uv": [384, 0], "uv_size": [5, 0]}, + "up": {"uv": [423, 69], "uv_size": [3, 5]}, + "down": {"uv": [423, 74], "uv_size": [3, -5]} + } + }, + { + "origin": [8.9, 33, -3.5], + "size": [0.1, 6, 7], + "pivot": [9, 36, 0], + "rotation": [0, 0, 45], + "uv": { + "north": {"uv": [384, 0], "uv_size": [0, 6]}, + "east": {"uv": [438, 23], "uv_size": [-7, 6]}, + "south": {"uv": [384, 0], "uv_size": [0, 6]}, + "west": {"uv": [431, 23], "uv_size": [7, 6]}, + "up": {"uv": [384, 0], "uv_size": [0, 7]}, + "down": {"uv": [384, 7], "uv_size": [0, -7]} + } + } + ] + }, + { + "name": "left_elbow", + "parent": "left_arm", + "pivot": [6.5, 26, 1], + "rotation": [-22.5, 0, 0], + "cubes": [ + { + "origin": [4.5, 17, -2], + "size": [4, 9, 4], + "uv": { + "north": {"uv": [392, 50], "uv_size": [4, 9]}, + "east": {"uv": [413, 51], "uv_size": [4, 9]}, + "south": {"uv": [417, 51], "uv_size": [4, 9]}, + "west": {"uv": [427, 53], "uv_size": [4, 9]}, + "up": {"uv": [451, 49], "uv_size": [4, 4]}, + "down": {"uv": [440, 71], "uv_size": [4, -4]} + } + }, + { + "origin": [5.5, 24, 0.5], + "size": [2, 3, 2], + "uv": { + "north": {"uv": [476, 2], "uv_size": [-2, 3]}, + "east": {"uv": [474, 5], "uv_size": [-2, 3]}, + "south": {"uv": [476, 5], "uv_size": [-2, 3]}, + "west": {"uv": [474, 2], "uv_size": [-2, 3]}, + "up": {"uv": [478, 4], "uv_size": [2, -2]}, + "down": {"uv": [476, 4], "uv_size": [2, -2]} + } + } + ] + }, + { + "name": "hand_boomstick", + "parent": "left_elbow", + "pivot": [6.5, 20, -2], + "cubes": [ + { + "origin": [4.5, 5, -5], + "size": [4, 15, 3], + "uv": { + "north": {"uv": [403, 18], "uv_size": [4, 15]}, + "east": {"uv": [399, 41], "uv_size": [3, 15]}, + "south": {"uv": [407, 18], "uv_size": [4, 15]}, + "west": {"uv": [410, 42], "uv_size": [3, 15]}, + "up": {"uv": [426, 70], "uv_size": [4, 3]}, + "down": {"uv": [436, 73], "uv_size": [4, -3]} + } + }, + { + "origin": [6.75, 4, -5.25], + "size": [2, 13, 2], + "uv": { + "north": {"uv": [412, 60], "uv_size": [2, 13]}, + "east": {"uv": [414, 60], "uv_size": [2, 13]}, + "south": {"uv": [416, 60], "uv_size": [2, 13]}, + "west": {"uv": [418, 60], "uv_size": [2, 13]}, + "up": {"uv": [388, 73], "uv_size": [2, 2]}, + "down": {"uv": [412, 75], "uv_size": [2, -2]} + } + }, + { + "origin": [4.25, 4, -5.25], + "size": [2, 13, 2], + "uv": { + "north": {"uv": [414, 60], "uv_size": [-2, 13]}, + "east": {"uv": [420, 60], "uv_size": [-2, 13]}, + "south": {"uv": [418, 60], "uv_size": [-2, 13]}, + "west": {"uv": [416, 60], "uv_size": [-2, 13]}, + "up": {"uv": [390, 73], "uv_size": [-2, 2]}, + "down": {"uv": [414, 75], "uv_size": [-2, -2]} + } + }, + { + "origin": [5, 17.5, -4], + "size": [3, 3, 7], + "pivot": [6.5, 19, -3], + "rotation": [22.5, 0, 0], + "uv": { + "north": {"uv": [456, 19], "uv_size": [3, 3]}, + "east": {"uv": [446, 26], "uv_size": [7, 3]}, + "south": {"uv": [404, 72], "uv_size": [3, 3]}, + "west": {"uv": [423, 62], "uv_size": [7, 3]}, + "up": {"uv": [430, 62], "uv_size": [3, 7]}, + "down": {"uv": [433, 69], "uv_size": [3, -7]} + } + } + ] + }, + { + "name": "right_arm", + "parent": "upper_torso", + "pivot": [-6, 32.5, 0], + "rotation": [0, 0, 22.5], + "cubes": [ + { + "origin": [-9, 26, -2], + "size": [5, 9, 4], + "uv": { + "north": {"uv": [426, 17], "uv_size": [5, 9]}, + "east": {"uv": [431, 53], "uv_size": [4, 9]}, + "south": {"uv": [413, 42], "uv_size": [5, 9]}, + "west": {"uv": [435, 53], "uv_size": [4, 9]}, + "up": {"uv": [447, 21], "uv_size": [5, 4]}, + "down": {"uv": [447, 49], "uv_size": [5, -4]} + } + }, + { + "origin": [-11, 28, -2], + "size": [4, 0.1, 4], + "pivot": [-9, 27, 0], + "rotation": [0, 45, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [4, 0]}, + "east": {"uv": [384, 0], "uv_size": [4, 0]}, + "south": {"uv": [384, 0], "uv_size": [4, 0]}, + "west": {"uv": [384, 0], "uv_size": [4, 0]}, + "up": {"uv": [452, 3], "uv_size": [4, 4]}, + "down": {"uv": [452, 7], "uv_size": [4, -4]} + } + }, + { + "origin": [-10, 29, -3], + "size": [4, 5, 6], + "uv": { + "north": {"uv": [445, 62], "uv_size": [-4, 5]}, + "east": {"uv": [412, 57], "uv_size": [-6, 5]}, + "south": {"uv": [405, 63], "uv_size": [-4, 5]}, + "west": {"uv": [402, 56], "uv_size": [-6, 5]}, + "up": {"uv": [449, 57], "uv_size": [-4, 6]}, + "down": {"uv": [388, 68], "uv_size": [-4, -6]} + } + }, + { + "origin": [-12, 29.41421, -2.5], + "size": [3, 0.1, 5], + "pivot": [-10, 30.41421, 0], + "rotation": [0, 0, 45], + "uv": { + "north": {"uv": [387, 0], "uv_size": [-3, 0]}, + "east": {"uv": [389, 0], "uv_size": [-5, 0]}, + "south": {"uv": [387, 0], "uv_size": [-3, 0]}, + "west": {"uv": [389, 0], "uv_size": [-5, 0]}, + "up": {"uv": [426, 69], "uv_size": [-3, 5]}, + "down": {"uv": [426, 74], "uv_size": [-3, -5]} + } + }, + { + "origin": [-10.1, 31.41421, -3.5], + "size": [0.1, 6, 7], + "pivot": [-9, 35.41421, 0], + "rotation": [0, 0, -45], + "uv": { + "north": {"uv": [384, 0], "uv_size": [0, 6]}, + "east": {"uv": [438, 23], "uv_size": [-7, 6]}, + "south": {"uv": [384, 0], "uv_size": [0, 6]}, + "west": {"uv": [431, 23], "uv_size": [7, 6]}, + "up": {"uv": [384, 0], "uv_size": [0, 7]}, + "down": {"uv": [384, 7], "uv_size": [0, -7]} + } + } + ] + }, + { + "name": "right_elbow", + "parent": "right_arm", + "pivot": [-6.5, 26, 1], + "rotation": [-22.5, 0, 0], + "cubes": [ + { + "origin": [-8.5, 17, -2], + "size": [4, 9, 4], + "uv": { + "north": {"uv": [438, 0], "uv_size": [4, 9]}, + "east": {"uv": [438, 9], "uv_size": [4, 9]}, + "south": {"uv": [402, 54], "uv_size": [4, 9]}, + "west": {"uv": [438, 18], "uv_size": [4, 9]}, + "up": {"uv": [399, 68], "uv_size": [4, 4]}, + "down": {"uv": [403, 72], "uv_size": [4, -4]} + } + }, + { + "origin": [-7.5, 24, 0.5], + "size": [2, 3, 2], + "uv": { + "north": {"uv": [474, 2], "uv_size": [2, 3]}, + "east": {"uv": [472, 2], "uv_size": [2, 3]}, + "south": {"uv": [474, 5], "uv_size": [2, 3]}, + "west": {"uv": [472, 5], "uv_size": [2, 3]}, + "up": {"uv": [480, 4], "uv_size": [-2, -2]}, + "down": {"uv": [478, 4], "uv_size": [-2, -2]} + } + } + ] + }, + { + "name": "axe", + "parent": "right_elbow", + "pivot": [-6.5, 19, -1], + "rotation": [45, 45, -15], + "cubes": [ + { + "origin": [-6.6, 10.5, -32], + "size": [0.1, 17, 19], + "uv": { + "north": {"uv": [384, 0], "uv_size": [0, 17]}, + "east": {"uv": [461, 111], "uv_size": [-19, 17]}, + "south": {"uv": [384, 0], "uv_size": [0, 17]}, + "west": {"uv": [442, 111], "uv_size": [19, 17]}, + "up": {"uv": [384, 0], "uv_size": [0, 19]}, + "down": {"uv": [384, 19], "uv_size": [0, -19]} + } + }, + { + "origin": [-7.5, 18, -26], + "size": [2, 2, 32], + "uv": { + "north": {"uv": [414, 73], "uv_size": [2, 2]}, + "east": {"uv": [421, 31], "uv_size": [24, 2]}, + "south": {"uv": [416, 73], "uv_size": [2, 2]}, + "west": {"uv": [421, 33], "uv_size": [24, 2]}, + "up": {"uv": [408, 33], "uv_size": [2, 24]}, + "down": {"uv": [421, 59], "uv_size": [2, -24]} + } + }, + { + "origin": [-7.5, 17.5, -31], + "size": [2, 3, 3], + "uv": { + "north": {"uv": [399, 65], "uv_size": [2, 3]}, + "east": {"uv": [456, 22], "uv_size": [3, 3]}, + "south": {"uv": [420, 72], "uv_size": [2, 3]}, + "west": {"uv": [407, 72], "uv_size": [3, 3]}, + "up": {"uv": [456, 52], "uv_size": [2, 3]}, + "down": {"uv": [456, 58], "uv_size": [2, -3]} + } + }, + { + "origin": [-6.5, 17.5, -32.5], + "size": [0, 4, 4], + "pivot": [-6.5, 19, -31], + "rotation": [-45, 0, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [0, 4]}, + "east": {"uv": [407, 68], "uv_size": [4, 4]}, + "south": {"uv": [384, 0], "uv_size": [0, 4]}, + "west": {"uv": [411, 68], "uv_size": [-4, 4]}, + "up": {"uv": [384, 0], "uv_size": [0, 4]}, + "down": {"uv": [384, 4], "uv_size": [0, -4]} + } + } + ] + }, + { + "name": "inactive_axe", + "parent": "right_elbow", + "pivot": [-6.5, 19, -1], + "rotation": [45, 45, -15], + "cubes": [ + { + "origin": [-7.5, 18, -26], + "size": [2, 2, 32], + "uv": { + "north": {"uv": [414, 73], "uv_size": [2, 2]}, + "east": {"uv": [421, 31], "uv_size": [24, 2]}, + "south": {"uv": [416, 73], "uv_size": [2, 2]}, + "west": {"uv": [421, 33], "uv_size": [24, 2]}, + "up": {"uv": [408, 33], "uv_size": [2, 24]}, + "down": {"uv": [421, 59], "uv_size": [2, -24]} + } + }, + { + "origin": [-7.5, 17.5, -29], + "size": [2, 3, 3], + "uv": { + "north": {"uv": [404, 75], "uv_size": [2, 3]}, + "east": {"uv": [459, 22], "uv_size": [3, 3]}, + "south": {"uv": [420, 75], "uv_size": [2, 3]}, + "west": {"uv": [407, 75], "uv_size": [3, 3]}, + "up": {"uv": [458, 52], "uv_size": [2, 3]}, + "down": {"uv": [458, 58], "uv_size": [2, -3]} + } + }, + { + "origin": [-6.5, 17.5, -30.5], + "size": [0, 4, 4], + "pivot": [-6.5, 19, -29], + "rotation": [-45, 0, 0], + "uv": { + "north": {"uv": [384, 0], "uv_size": [0, 4]}, + "east": {"uv": [407, 68], "uv_size": [4, 4]}, + "south": {"uv": [384, 0], "uv_size": [0, 4]}, + "west": {"uv": [411, 68], "uv_size": [-4, 4]}, + "up": {"uv": [384, 0], "uv_size": [0, 4]}, + "down": {"uv": [384, 4], "uv_size": [0, -4]} + } + } + ] + }, + { + "name": "b_torso", + "parent": "torso", + "pivot": [0, 50, 0], + "cubes": [ + { + "origin": [-8, 16, -8], + "size": [16, 16, 16], + "uv": { + "north": {"uv": [384, 0], "uv_size": [16, 16]}, + "east": {"uv": [384, 0], "uv_size": [16, 16]}, + "south": {"uv": [384, 0], "uv_size": [16, 16]}, + "west": {"uv": [384, 0], "uv_size": [16, 16]}, + "up": {"uv": [400, 16], "uv_size": [-16, -16]}, + "down": {"uv": [400, 16], "uv_size": [-16, -16]} + } + } + ] + }, + { + "name": "b_head", + "parent": "torso", + "pivot": [0, 66, 0], + "cubes": [ + { + "origin": [-4, 32, -4], + "size": [8, 8, 8], + "uv": { + "north": {"uv": [384, 0], "uv_size": [8, 8]}, + "east": {"uv": [384, 0], "uv_size": [8, 8]}, + "south": {"uv": [384, 0], "uv_size": [8, 8]}, + "west": {"uv": [384, 0], "uv_size": [8, 8]}, + "up": {"uv": [392, 8], "uv_size": [-8, -8]}, + "down": {"uv": [392, 8], "uv_size": [-8, -8]} + } + } + ] + }, + { + "name": "left_leg", + "parent": "body2", + "pivot": [2.5, 21, 0], + "rotation": [0, -10, -5], + "cubes": [ + { + "origin": [0, 11, -2.5], + "size": [5, 10, 5], + "uv": { + "north": {"uv": [411, 32], "uv_size": [5, 10]}, + "east": {"uv": [416, 32], "uv_size": [5, 10]}, + "south": {"uv": [403, 33], "uv_size": [5, 10]}, + "west": {"uv": [384, 34], "uv_size": [5, 10]}, + "up": {"uv": [426, 26], "uv_size": [5, 5]}, + "down": {"uv": [442, 26], "uv_size": [5, -5]} + } + }, + { + "origin": [2, 17, -2.75], + "size": [3, 5, 0.1], + "pivot": [4, 18.5, -2.75], + "rotation": [22.5, 0, 0], + "uv": { + "north": {"uv": [456, 53], "uv_size": [-3, 5]}, + "east": {"uv": [384, 0], "uv_size": [0, 5]}, + "south": {"uv": [453, 53], "uv_size": [3, 5]}, + "west": {"uv": [384, 0], "uv_size": [0, 5]}, + "up": {"uv": [384, 0], "uv_size": [3, 0]}, + "down": {"uv": [384, 0], "uv_size": [3, 0]} + } + }, + { + "origin": [2, 15, -2.75], + "size": [3, 4, 0.1], + "pivot": [4, 15.5, -2.75], + "rotation": [0, 0, 22.5], + "uv": { + "north": {"uv": [457, 68], "uv_size": [-3, 4]}, + "east": {"uv": [384, 0], "uv_size": [0, 4]}, + "south": {"uv": [454, 68], "uv_size": [3, 4]}, + "west": {"uv": [384, 0], "uv_size": [0, 4]}, + "up": {"uv": [384, 0], "uv_size": [3, 0]}, + "down": {"uv": [384, 0], "uv_size": [3, 0]} + } + } + ] + }, + { + "name": "left_knee", + "parent": "left_leg", + "pivot": [2.5, 11, -1.5], + "rotation": [5, 0, 0], + "cubes": [ + { + "origin": [0.5, 0, -2.5], + "size": [4, 11, 4], + "uv": { + "north": {"uv": [402, 43], "uv_size": [4, 11]}, + "east": {"uv": [384, 44], "uv_size": [4, 11]}, + "south": {"uv": [428, 35], "uv_size": [4, 11]}, + "west": {"uv": [423, 44], "uv_size": [4, 11]}, + "up": {"uv": [452, 45], "uv_size": [4, 4]}, + "down": {"uv": [444, 72], "uv_size": [4, -4]} + } + }, + { + "origin": [1, 8, -3.5], + "size": [3, 5, 2], + "uv": { + "north": {"uv": [453, 63], "uv_size": [3, 5]}, + "east": {"uv": [391, 72], "uv_size": [2, 5]}, + "south": {"uv": [448, 69], "uv_size": [3, 5]}, + "west": {"uv": [456, 8], "uv_size": [2, 5]}, + "up": {"uv": [453, 19], "uv_size": [3, 2]}, + "down": {"uv": [456, 60], "uv_size": [3, -2]} + } + } + ] + }, + { + "name": "leg_boomstick", + "parent": "left_leg", + "pivot": [6.5, 21, 0], + "cubes": [ + { + "origin": [5, 19, -0.5], + "size": [3, 3, 7], + "pivot": [6.5, 20.5, 0.5], + "rotation": [22.5, 0, 0], + "uv": { + "north": {"uv": [456, 19], "uv_size": [3, 3]}, + "east": {"uv": [446, 26], "uv_size": [7, 3]}, + "south": {"uv": [404, 72], "uv_size": [3, 3]}, + "west": {"uv": [423, 62], "uv_size": [7, 3]}, + "up": {"uv": [430, 62], "uv_size": [3, 7]}, + "down": {"uv": [433, 69], "uv_size": [3, -7]} + } + }, + { + "origin": [4.5, 6.5, -1.5], + "size": [4, 15, 3], + "uv": { + "north": {"uv": [403, 18], "uv_size": [4, 15]}, + "east": {"uv": [399, 41], "uv_size": [3, 15]}, + "south": {"uv": [407, 18], "uv_size": [4, 15]}, + "west": {"uv": [410, 42], "uv_size": [3, 15]}, + "up": {"uv": [426, 70], "uv_size": [4, 3]}, + "down": {"uv": [436, 73], "uv_size": [4, -3]} + } + }, + { + "origin": [4.25, 5.5, -1.75], + "size": [2, 13, 2], + "uv": { + "north": {"uv": [414, 60], "uv_size": [-2, 13]}, + "east": {"uv": [420, 60], "uv_size": [-2, 13]}, + "south": {"uv": [418, 60], "uv_size": [-2, 13]}, + "west": {"uv": [416, 60], "uv_size": [-2, 13]}, + "up": {"uv": [390, 73], "uv_size": [-2, 2]}, + "down": {"uv": [414, 75], "uv_size": [-2, -2]} + } + }, + { + "origin": [6.75, 5.5, -1.75], + "size": [2, 13, 2], + "uv": { + "north": {"uv": [412, 60], "uv_size": [2, 13]}, + "east": {"uv": [414, 60], "uv_size": [2, 13]}, + "south": {"uv": [416, 60], "uv_size": [2, 13]}, + "west": {"uv": [418, 60], "uv_size": [2, 13]}, + "up": {"uv": [388, 73], "uv_size": [2, 2]}, + "down": {"uv": [412, 75], "uv_size": [2, -2]} + } + } + ] + }, + { + "name": "right_leg", + "parent": "body2", + "pivot": [-2.5, 21, 0], + "rotation": [0, 10, 5], + "cubes": [ + { + "origin": [-5, 11, -2.5], + "size": [5, 10, 5], + "uv": { + "north": {"uv": [416, 32], "uv_size": [-5, 10]}, + "east": {"uv": [389, 34], "uv_size": [-5, 10]}, + "south": {"uv": [408, 33], "uv_size": [-5, 10]}, + "west": {"uv": [421, 32], "uv_size": [-5, 10]}, + "up": {"uv": [431, 26], "uv_size": [-5, 5]}, + "down": {"uv": [447, 26], "uv_size": [-5, -5]} + } + }, + { + "origin": [-5, 17, -2.75], + "size": [3, 5, 0.1], + "pivot": [-4, 18.5, -2.75], + "rotation": [22.5, 0, 0], + "uv": { + "north": {"uv": [453, 53], "uv_size": [3, 5]}, + "east": {"uv": [384, 0], "uv_size": [0, 5]}, + "south": {"uv": [456, 53], "uv_size": [-3, 5]}, + "west": {"uv": [384, 0], "uv_size": [0, 5]}, + "up": {"uv": [387, 0], "uv_size": [-3, 0]}, + "down": {"uv": [387, 0], "uv_size": [-3, 0]} + } + }, + { + "origin": [-5, 15, -2.75], + "size": [3, 4, 0.1], + "pivot": [-4, 15.5, -2.75], + "rotation": [0, 0, -22.5], + "uv": { + "north": {"uv": [454, 68], "uv_size": [3, 4]}, + "east": {"uv": [384, 0], "uv_size": [0, 4]}, + "south": {"uv": [457, 68], "uv_size": [-3, 4]}, + "west": {"uv": [384, 0], "uv_size": [0, 4]}, + "up": {"uv": [387, 0], "uv_size": [-3, 0]}, + "down": {"uv": [387, 0], "uv_size": [-3, 0]} + } + } + ] + }, + { + "name": "right_knee", + "parent": "right_leg", + "pivot": [-2.5, 11, -1.5], + "rotation": [5, 0, 0], + "cubes": [ + { + "origin": [-4.5, 0, -2.5], + "size": [4, 11, 4], + "uv": { + "north": {"uv": [406, 43], "uv_size": [-4, 11]}, + "east": {"uv": [427, 44], "uv_size": [-4, 11]}, + "south": {"uv": [432, 35], "uv_size": [-4, 11]}, + "west": {"uv": [388, 44], "uv_size": [-4, 11]}, + "up": {"uv": [456, 45], "uv_size": [-4, 4]}, + "down": {"uv": [448, 72], "uv_size": [-4, -4]} + } + }, + { + "origin": [-4, 8, -3.5], + "size": [3, 5, 2], + "uv": { + "north": {"uv": [456, 63], "uv_size": [-3, 5]}, + "east": {"uv": [458, 8], "uv_size": [-2, 5]}, + "south": {"uv": [451, 69], "uv_size": [-3, 5]}, + "west": {"uv": [393, 72], "uv_size": [-2, 5]}, + "up": {"uv": [456, 19], "uv_size": [-3, 2]}, + "down": {"uv": [459, 60], "uv_size": [-3, -2]} + } + } + ] + }, + { + "name": "h_slash", + "parent": "body2", + "pivot": [0, 24, -32] + }, + { + "name": "h_slash2", + "parent": "h_slash", + "pivot": [0, 24, -32] + }, + { + "name": "h_slash3", + "parent": "h_slash2", + "pivot": [0, 24, -32] + }, + { + "name": "h_slash4", + "parent": "h_slash3", + "pivot": [0, 24, -32] + }, + { + "name": "h_slash5", + "parent": "h_slash4", + "pivot": [0, 24, -32] + }, + { + "name": "h_slash6", + "parent": "h_slash5", + "pivot": [0, 24, -32] + }, + { + "name": "h_slash7", + "parent": "h_slash6", + "pivot": [0, 24, -32] + }, + { + "name": "h_slash8", + "parent": "h_slash7", + "pivot": [0, 24, -32] + }, + { + "name": "h_slash9", + "parent": "h_slash8", + "pivot": [0, 24, -32] + }, + { + "name": "h_slash10", + "parent": "h_slash9", + "pivot": [0, 24, -32], + "cubes": [ + { + "origin": [-0.1, 10, -48], + "size": [0.1, 32, 32], + "uv": { + "north": {"uv": [384, 0], "uv_size": [0, 32]}, + "east": {"uv": [512, 96], "uv_size": [-32, 32]}, + "south": {"uv": [384, 0], "uv_size": [0, 32]}, + "west": {"uv": [480, 96], "uv_size": [32, 32]}, + "up": {"uv": [384, 32], "uv_size": [0, -32]}, + "down": {"uv": [384, 32], "uv_size": [0, -32]} + } + } + ] + }, + { + "name": "dreadnought_portal", + "pivot": [0, 0, 0] + }, + { + "name": "body", + "parent": "dreadnought_portal", + "pivot": [0, 0, 0] + }, + { + "name": "black", + "parent": "body", + "pivot": [0, 24, 0], + "cubes": [ + { + "origin": [-46, -22, 0], + "size": [92, 92, 0.1], + "uv": { + "north": {"uv": [128, 0], "uv_size": [128, 128]}, + "east": {"uv": [128, 0], "uv_size": [0, 32]}, + "south": {"uv": [256, 0], "uv_size": [-128, 128]}, + "west": {"uv": [128, 0], "uv_size": [0, 32]}, + "up": {"uv": [160, 0], "uv_size": [-32, 0]}, + "down": {"uv": [160, 0], "uv_size": [-32, 0]} + } + } + ] + }, + { + "name": "cluster1", + "parent": "body", + "pivot": [0, 24, 0] + }, + { + "name": "cloud1", + "parent": "cluster1", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud2", + "parent": "cluster1", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud3", + "parent": "cluster1", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud4", + "parent": "cluster1", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster2", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 10] + }, + { + "name": "cloud5", + "parent": "cluster2", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud6", + "parent": "cluster2", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud7", + "parent": "cluster2", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud8", + "parent": "cluster2", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud9", + "parent": "cluster2", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster3", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 22.5] + }, + { + "name": "cloud10", + "parent": "cluster3", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud11", + "parent": "cluster3", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud12", + "parent": "cluster3", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud13", + "parent": "cluster3", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster4", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 32.5] + }, + { + "name": "cloud14", + "parent": "cluster4", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud15", + "parent": "cluster4", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud16", + "parent": "cluster4", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud17", + "parent": "cluster4", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud18", + "parent": "cluster4", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster5", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 45] + }, + { + "name": "cloud19", + "parent": "cluster5", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud20", + "parent": "cluster5", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud21", + "parent": "cluster5", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud22", + "parent": "cluster5", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster6", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 55] + }, + { + "name": "cloud23", + "parent": "cluster6", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud24", + "parent": "cluster6", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud25", + "parent": "cluster6", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud26", + "parent": "cluster6", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud27", + "parent": "cluster6", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster7", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 67.5] + }, + { + "name": "cloud28", + "parent": "cluster7", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud29", + "parent": "cluster7", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud30", + "parent": "cluster7", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud31", + "parent": "cluster7", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster8", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 77.5] + }, + { + "name": "cloud32", + "parent": "cluster8", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud33", + "parent": "cluster8", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud34", + "parent": "cluster8", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud35", + "parent": "cluster8", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud36", + "parent": "cluster8", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster9", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 90] + }, + { + "name": "cloud37", + "parent": "cluster9", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud38", + "parent": "cluster9", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud39", + "parent": "cluster9", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud40", + "parent": "cluster9", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster10", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 100] + }, + { + "name": "cloud41", + "parent": "cluster10", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud42", + "parent": "cluster10", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud43", + "parent": "cluster10", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud44", + "parent": "cluster10", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud45", + "parent": "cluster10", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster11", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 112.5] + }, + { + "name": "cloud46", + "parent": "cluster11", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud47", + "parent": "cluster11", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud48", + "parent": "cluster11", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud49", + "parent": "cluster11", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster12", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 122.5] + }, + { + "name": "cloud50", + "parent": "cluster12", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud51", + "parent": "cluster12", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud52", + "parent": "cluster12", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud53", + "parent": "cluster12", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud54", + "parent": "cluster12", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster13", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 135] + }, + { + "name": "cloud55", + "parent": "cluster13", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud56", + "parent": "cluster13", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud57", + "parent": "cluster13", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud58", + "parent": "cluster13", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster14", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 145] + }, + { + "name": "cloud59", + "parent": "cluster14", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud60", + "parent": "cluster14", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud61", + "parent": "cluster14", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud62", + "parent": "cluster14", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud63", + "parent": "cluster14", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster15", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 157.5] + }, + { + "name": "cloud64", + "parent": "cluster15", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud65", + "parent": "cluster15", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud66", + "parent": "cluster15", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud67", + "parent": "cluster15", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster16", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, 167.5] + }, + { + "name": "cloud68", + "parent": "cluster16", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud69", + "parent": "cluster16", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud70", + "parent": "cluster16", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud71", + "parent": "cluster16", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud72", + "parent": "cluster16", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster17", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -180] + }, + { + "name": "cloud73", + "parent": "cluster17", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud74", + "parent": "cluster17", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud75", + "parent": "cluster17", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud76", + "parent": "cluster17", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster18", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -170] + }, + { + "name": "cloud77", + "parent": "cluster18", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud78", + "parent": "cluster18", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud79", + "parent": "cluster18", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud80", + "parent": "cluster18", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud81", + "parent": "cluster18", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster19", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -157.5] + }, + { + "name": "cloud82", + "parent": "cluster19", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud83", + "parent": "cluster19", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud84", + "parent": "cluster19", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud85", + "parent": "cluster19", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster20", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -147.5] + }, + { + "name": "cloud86", + "parent": "cluster20", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud87", + "parent": "cluster20", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud88", + "parent": "cluster20", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud89", + "parent": "cluster20", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud90", + "parent": "cluster20", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster21", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -135] + }, + { + "name": "cloud91", + "parent": "cluster21", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud92", + "parent": "cluster21", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud93", + "parent": "cluster21", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud94", + "parent": "cluster21", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster22", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -125] + }, + { + "name": "cloud95", + "parent": "cluster22", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud96", + "parent": "cluster22", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud97", + "parent": "cluster22", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud98", + "parent": "cluster22", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud99", + "parent": "cluster22", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster23", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -112.5] + }, + { + "name": "cloud100", + "parent": "cluster23", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud101", + "parent": "cluster23", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud102", + "parent": "cluster23", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud103", + "parent": "cluster23", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster24", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -102.5] + }, + { + "name": "cloud104", + "parent": "cluster24", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud105", + "parent": "cluster24", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud106", + "parent": "cluster24", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud107", + "parent": "cluster24", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud108", + "parent": "cluster24", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster25", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -90] + }, + { + "name": "cloud109", + "parent": "cluster25", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud110", + "parent": "cluster25", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud111", + "parent": "cluster25", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud112", + "parent": "cluster25", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster26", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -80] + }, + { + "name": "cloud113", + "parent": "cluster26", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud114", + "parent": "cluster26", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud115", + "parent": "cluster26", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud116", + "parent": "cluster26", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud117", + "parent": "cluster26", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster27", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -67.5] + }, + { + "name": "cloud118", + "parent": "cluster27", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud119", + "parent": "cluster27", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud120", + "parent": "cluster27", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud121", + "parent": "cluster27", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster28", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -57.5] + }, + { + "name": "cloud122", + "parent": "cluster28", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud123", + "parent": "cluster28", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud124", + "parent": "cluster28", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud125", + "parent": "cluster28", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud126", + "parent": "cluster28", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster29", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -45] + }, + { + "name": "cloud127", + "parent": "cluster29", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud128", + "parent": "cluster29", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud129", + "parent": "cluster29", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud130", + "parent": "cluster29", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster30", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -35] + }, + { + "name": "cloud131", + "parent": "cluster30", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud132", + "parent": "cluster30", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud133", + "parent": "cluster30", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud134", + "parent": "cluster30", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud135", + "parent": "cluster30", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cluster31", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -22.5] + }, + { + "name": "cloud136", + "parent": "cluster31", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [10, 48], "uv_size": [8, 8]}, + "east": {"uv": [34, 52], "uv_size": [8, 8]}, + "south": {"uv": [42, 52], "uv_size": [8, 8]}, + "west": {"uv": [50, 52], "uv_size": [8, 8]}, + "up": {"uv": [54, 42], "uv_size": [8, 8]}, + "down": {"uv": [10, 64], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud137", + "parent": "cluster31", + "pivot": [3, 64, 1], + "cubes": [ + { + "origin": [1, 62, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [56, 10], "uv_size": [8, 8]}, + "east": {"uv": [18, 56], "uv_size": [8, 8]}, + "south": {"uv": [56, 18], "uv_size": [8, 8]}, + "west": {"uv": [26, 56], "uv_size": [8, 8]}, + "up": {"uv": [56, 26], "uv_size": [8, 8]}, + "down": {"uv": [56, 42], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud138", + "parent": "cluster31", + "pivot": [3, 69, 0], + "cubes": [ + { + "origin": [0, 66, -3], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [0, 0], "uv_size": [12, 12]}, + "east": {"uv": [0, 12], "uv_size": [12, 12]}, + "south": {"uv": [12, 0], "uv_size": [12, 12]}, + "west": {"uv": [12, 12], "uv_size": [12, 12]}, + "up": {"uv": [0, 24], "uv_size": [12, 12]}, + "down": {"uv": [24, 12], "uv_size": [12, -12]} + } + } + ] + }, + { + "name": "cloud139", + "parent": "cluster31", + "pivot": [-2, 64, 0], + "cubes": [ + { + "origin": [-4.5, 61.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [36, 12], "uv_size": [10, 10]}, + "east": {"uv": [36, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 36], "uv_size": [10, 10]}, + "west": {"uv": [36, 32], "uv_size": [10, 10]}, + "up": {"uv": [34, 42], "uv_size": [10, 10]}, + "down": {"uv": [44, 52], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cluster32", + "parent": "body", + "pivot": [0, 24, 0], + "rotation": [0, 0, -12.5] + }, + { + "name": "cloud140", + "parent": "cluster32", + "pivot": [0, 67, 0], + "cubes": [ + { + "origin": [-2, 65, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [0, 58], "uv_size": [8, 8]}, + "east": {"uv": [58, 0], "uv_size": [8, 8]}, + "south": {"uv": [58, 50], "uv_size": [8, 8]}, + "west": {"uv": [58, 58], "uv_size": [8, 8]}, + "up": {"uv": [34, 60], "uv_size": [8, 8]}, + "down": {"uv": [42, 68], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud141", + "parent": "cluster32", + "pivot": [4, 65, 1], + "cubes": [ + { + "origin": [2, 63, -1], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [50, 60], "uv_size": [8, 8]}, + "east": {"uv": [62, 42], "uv_size": [8, 8]}, + "south": {"uv": [8, 64], "uv_size": [8, 8]}, + "west": {"uv": [64, 8], "uv_size": [8, 8]}, + "up": {"uv": [16, 64], "uv_size": [8, 8]}, + "down": {"uv": [64, 24], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud142", + "parent": "cluster32", + "pivot": [2, 70, -0.5], + "cubes": [ + { + "origin": [-0.5, 67.5, -2.5], + "size": [5, 5, 5], + "uv": { + "north": {"uv": [46, 12], "uv_size": [10, 10]}, + "east": {"uv": [46, 22], "uv_size": [10, 10]}, + "south": {"uv": [24, 46], "uv_size": [10, 10]}, + "west": {"uv": [46, 32], "uv_size": [10, 10]}, + "up": {"uv": [0, 48], "uv_size": [10, 10]}, + "down": {"uv": [48, 10], "uv_size": [10, -10]} + } + } + ] + }, + { + "name": "cloud143", + "parent": "cluster32", + "pivot": [6, 69, 0], + "cubes": [ + { + "origin": [4, 67, -2], + "size": [4, 4, 4], + "uv": { + "north": {"uv": [24, 64], "uv_size": [8, 8]}, + "east": {"uv": [64, 24], "uv_size": [8, 8]}, + "south": {"uv": [64, 32], "uv_size": [8, 8]}, + "west": {"uv": [0, 66], "uv_size": [8, 8]}, + "up": {"uv": [66, 0], "uv_size": [8, 8]}, + "down": {"uv": [66, 58], "uv_size": [8, -8]} + } + } + ] + }, + { + "name": "cloud144", + "parent": "cluster32", + "pivot": [0, 64, 0.5], + "cubes": [ + { + "origin": [-3, 61, -2.5], + "size": [6, 6, 6], + "uv": { + "north": {"uv": [12, 24], "uv_size": [12, 12]}, + "east": {"uv": [24, 12], "uv_size": [12, 12]}, + "south": {"uv": [24, 24], "uv_size": [12, 12]}, + "west": {"uv": [0, 36], "uv_size": [12, 12]}, + "up": {"uv": [36, 0], "uv_size": [12, 12]}, + "down": {"uv": [12, 48], "uv_size": [12, -12]} + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/geo/item/doomicorn.geo.json b/common/src/main/resources/assets/azurelib/geo/item/doomicorn.geo.json new file mode 100644 index 000000000..2f4ec1a72 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/geo/item/doomicorn.geo.json @@ -0,0 +1,17113 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.unknown", + "texture_width": 64, + "texture_height": 64, + "visible_bounds_width": 4, + "visible_bounds_height": 4.5, + "visible_bounds_offset": [ + 0, + 1.75, + 0 + ] + }, + "bones": [ + { + "name": "bipedHead", + "pivot": [ + 0, + 24, + 0 + ] + }, + { + "name": "armorHead", + "parent": "bipedHead", + "pivot": [ + 0, + 24, + 0 + ] + }, + { + "name": "horn", + "parent": "armorHead", + "pivot": [ + 0, + 33, + -3 + ] + }, + { + "name": "group3", + "parent": "horn", + "pivot": [ + 8, + 34.7, + -12 + ], + "cubes": [ + { + "origin": [ + -0.75, + 38, + -5.75 + ], + "size": [ + 1.5, + 2, + 1.5 + ], + "pivot": [ + 0, + 38.7, + -5 + ], + "rotation": [ + 22.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 25, + 23 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 25, + 23 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 24, + 24 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 24, + 25 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 24, + 25 + ], + "uv_size": [ + 1, + 1 + ] + } + } + }, + { + "origin": [ + -1, + 35, + -6 + ], + "size": [ + 2, + 3, + 2 + ], + "pivot": [ + 0, + 38.7, + -5 + ], + "rotation": [ + 22.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 23, + 23 + ], + "uv_size": [ + 2, + 3 + ] + }, + "east": { + "uv": [ + 23, + 24 + ], + "uv_size": [ + 2, + 3 + ] + }, + "south": { + "uv": [ + 23, + 24 + ], + "uv_size": [ + 2, + 3 + ] + }, + "west": { + "uv": [ + 23, + 22 + ], + "uv_size": [ + 2, + 3 + ] + }, + "up": { + "uv": [ + 23, + 23 + ], + "uv_size": [ + 2, + 2 + ] + } + } + }, + { + "origin": [ + -0.4, + 40, + -5.4 + ], + "size": [ + 0.8, + 1.2, + 0.8 + ], + "pivot": [ + 0, + 38.7, + -5 + ], + "rotation": [ + 22.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 24, + 23 + ], + "uv_size": [ + 0, + 1 + ] + }, + "east": { + "uv": [ + 25, + 25 + ], + "uv_size": [ + 0, + 1 + ] + }, + "south": { + "uv": [ + 25, + 25 + ], + "uv_size": [ + 0, + 1 + ] + }, + "west": { + "uv": [ + 25, + 23 + ], + "uv_size": [ + 0, + 1 + ] + }, + "up": { + "uv": [ + 26, + 26 + ], + "uv_size": [ + 0, + 0 + ] + } + } + } + ] + }, + { + "name": "bone2", + "parent": "horn", + "pivot": [ + 8, + 31.7, + -16.5 + ], + "cubes": [ + { + "origin": [ + -2.5, + 32.7, + -6 + ], + "size": [ + 5, + 3, + 7 + ], + "uv": { + "north": { + "uv": [ + 10, + 3 + ], + "uv_size": [ + 5, + 3 + ] + }, + "east": { + "uv": [ + 7, + 0 + ], + "uv_size": [ + -7, + 3 + ] + }, + "south": { + "uv": [ + 10, + 4 + ], + "uv_size": [ + 5, + 3 + ] + }, + "west": { + "uv": [ + 0, + 0 + ], + "uv_size": [ + 7, + 3 + ] + }, + "up": { + "uv": [ + 9, + 1 + ], + "uv_size": [ + 5, + 7 + ] + }, + "down": { + "uv": [ + 9, + 8 + ], + "uv_size": [ + 5, + -7 + ] + } + } + }, + { + "origin": [ + -0.5, + 35.7, + -2.3 + ], + "size": [ + 1, + 1.2, + 3.3 + ], + "uv": { + "north": { + "uv": [ + 21, + 23 + ], + "uv_size": [ + 5, + 3 + ] + }, + "east": { + "uv": [ + 23, + 24 + ], + "uv_size": [ + 3, + 3 + ] + }, + "south": { + "uv": [ + 22, + 23 + ], + "uv_size": [ + 5, + 3 + ] + }, + "west": { + "uv": [ + 22, + 23 + ], + "uv_size": [ + 5, + 3 + ] + }, + "up": { + "uv": [ + 22, + 22 + ], + "uv_size": [ + 4, + 5 + ] + } + } + }, + { + "origin": [ + -0.5, + 33, + -2.1 + ], + "size": [ + 1, + 2.2, + 5.5 + ], + "pivot": [ + -2.5, + 32.5, + 1.4 + ], + "rotation": [ + -47.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 21, + 23 + ], + "uv_size": [ + 5, + 3 + ] + }, + "east": { + "uv": [ + 23, + 24 + ], + "uv_size": [ + 3, + 3 + ] + }, + "west": { + "uv": [ + 22, + 23 + ], + "uv_size": [ + 5, + 3 + ] + } + } + }, + { + "origin": [ + 0.6, + 34.8, + -0.9 + ], + "size": [ + 1.8, + 2.8, + 0.8 + ], + "uv": { + "north": { + "uv": [ + 10, + 3 + ], + "uv_size": [ + 2, + 3 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 3 + ] + }, + "south": { + "uv": [ + 11, + 2 + ], + "uv_size": [ + 2, + 3 + ] + }, + "west": { + "uv": [ + 14, + 3 + ], + "uv_size": [ + 1, + 3 + ] + }, + "up": { + "uv": [ + 10, + 3 + ], + "uv_size": [ + 2, + 1 + ] + } + } + }, + { + "origin": [ + -2.4, + 34.8, + -0.9 + ], + "size": [ + 1.8, + 2.8, + 0.8 + ], + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 2, + 3 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 3 + ] + }, + "south": { + "uv": [ + 11, + 2 + ], + "uv_size": [ + 2, + 3 + ] + }, + "west": { + "uv": [ + 12, + 2 + ], + "uv_size": [ + 1, + 3 + ] + }, + "up": { + "uv": [ + 10, + 3 + ], + "uv_size": [ + 2, + 1 + ] + } + } + }, + { + "origin": [ + -2, + 32.7, + -8 + ], + "size": [ + 4, + 3, + 2 + ], + "uv": { + "north": { + "uv": [ + 10, + 3 + ], + "uv_size": [ + 4, + 3 + ] + }, + "east": { + "uv": [ + 12, + 3 + ], + "uv_size": [ + 2, + 3 + ] + }, + "west": { + "uv": [ + 12, + 2 + ], + "uv_size": [ + 2, + 3 + ] + }, + "up": { + "uv": [ + 11, + 4 + ], + "uv_size": [ + 4, + 2 + ] + }, + "down": { + "uv": [ + 10, + 5 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + -1, + 32.7, + 0.5 + ], + "size": [ + 2, + 1.9, + 2 + ], + "uv": { + "east": { + "uv": [ + 11, + 0 + ], + "uv_size": [ + 2, + 8 + ] + }, + "south": { + "uv": [ + 11, + 0 + ], + "uv_size": [ + 2, + 8 + ] + }, + "west": { + "uv": [ + 11, + 0 + ], + "uv_size": [ + 2, + 8 + ] + }, + "up": { + "uv": [ + 13, + 3 + ], + "uv_size": [ + 2, + 2 + ] + } + } + } + ] + }, + { + "name": "bone5", + "parent": "armorHead", + "pivot": [ + 0, + 0, + 0 + ], + "cubes": [ + { + "origin": [ + 2, + 24, + -5 + ], + "size": [ + 3, + 2, + 1 + ], + "uv": { + "north": { + "uv": [ + 46, + 14 + ], + "uv_size": [ + 3, + 2 + ] + }, + "west": { + "uv": [ + 48, + 14 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 41, + 14 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 46, + 16 + ], + "uv_size": [ + 3, + -1 + ] + } + } + }, + { + "origin": [ + -5, + 24, + -5 + ], + "size": [ + 3, + 2, + 1 + ], + "uv": { + "north": { + "uv": [ + 39, + 14 + ], + "uv_size": [ + 3, + 2 + ] + }, + "east": { + "uv": [ + 39, + 14 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 41, + 14 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 39, + 16 + ], + "uv_size": [ + 3, + -1 + ] + } + } + }, + { + "origin": [ + -5, + 26, + -5 + ], + "size": [ + 2, + 1, + 1 + ], + "uv": { + "north": { + "uv": [ + 39, + 13 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 39, + 13 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 39, + 13 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 41, + 14 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -5, + 27, + -5 + ], + "size": [ + 1, + 3, + 1 + ], + "uv": { + "north": { + "uv": [ + 39, + 10 + ], + "uv_size": [ + 1, + 3 + ] + }, + "east": { + "uv": [ + 39, + 10 + ], + "uv_size": [ + 1, + 3 + ] + }, + "west": { + "uv": [ + 39, + 10 + ], + "uv_size": [ + 1, + 3 + ] + } + } + }, + { + "origin": [ + 4, + 27, + -5 + ], + "size": [ + 1, + 3, + 1 + ], + "uv": { + "north": { + "uv": [ + 39, + 10 + ], + "uv_size": [ + 1, + 3 + ] + }, + "east": { + "uv": [ + 39, + 10 + ], + "uv_size": [ + 1, + 3 + ] + }, + "west": { + "uv": [ + 48, + 10 + ], + "uv_size": [ + 1, + 3 + ] + } + } + }, + { + "origin": [ + -5, + 30, + -5 + ], + "size": [ + 10, + 1, + 1 + ], + "uv": { + "north": { + "uv": [ + 39, + 9 + ], + "uv_size": [ + 10, + 1 + ] + }, + "east": { + "uv": [ + 39, + 9 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 48, + 9 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 49, + 10 + ], + "uv_size": [ + -10, + -1 + ] + } + } + }, + { + "origin": [ + -5, + 31, + -5 + ], + "size": [ + 10, + 2, + 1 + ], + "uv": { + "north": { + "uv": [ + 59, + 8 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 59, + 8 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 59, + 8 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 61, + 9 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -2, + 29, + -5 + ], + "size": [ + 4, + 1, + 1 + ], + "uv": { + "north": { + "uv": [ + 42, + 10 + ], + "uv_size": [ + 4, + 1 + ] + }, + "east": { + "uv": [ + 42, + 10 + ], + "uv_size": [ + 4, + 1 + ] + }, + "west": { + "uv": [ + 42, + 10 + ], + "uv_size": [ + 4, + 1 + ] + }, + "down": { + "uv": [ + 46, + 11 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + 3, + 26, + -5 + ], + "size": [ + 2, + 1, + 1 + ], + "uv": { + "north": { + "uv": [ + 47, + 13 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 39, + 13 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 48, + 13 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 49, + 14 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -2, + 24, + -5 + ], + "size": [ + 4, + 2, + 9 + ], + "uv": { + "north": { + "uv": [ + 42, + 14 + ], + "uv_size": [ + 4, + 2 + ] + }, + "up": { + "uv": [ + 41, + 14 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 46, + 16 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + -1, + 26, + -5 + ], + "size": [ + 2, + 1, + 1 + ], + "uv": { + "north": { + "uv": [ + 43, + 13 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 8, + 6 + ], + "uv_size": [ + 8, + 1 + ] + }, + "west": { + "uv": [ + 8, + 6 + ], + "uv_size": [ + 8, + 1 + ] + }, + "up": { + "uv": [ + 16, + 7 + ], + "uv_size": [ + -8, + -1 + ] + } + } + }, + { + "origin": [ + -4, + 27, + -4.5 + ], + "size": [ + 8, + 2, + 0.1 + ], + "uv": { + "north": { + "uv": [ + 40, + 11 + ], + "uv_size": [ + 8, + 2 + ] + }, + "south": { + "uv": [ + 42, + 11 + ], + "uv_size": [ + 4, + 2 + ] + } + } + }, + { + "origin": [ + 1, + 26, + -4.5 + ], + "size": [ + 2, + 1, + 0.1 + ], + "uv": { + "north": { + "uv": [ + 45, + 13 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 42, + 11 + ], + "uv_size": [ + 4, + 2 + ] + } + } + }, + { + "origin": [ + -3, + 26, + -4.5 + ], + "size": [ + 2, + 1, + 0.1 + ], + "uv": { + "north": { + "uv": [ + 41, + 13 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 42, + 11 + ], + "uv_size": [ + 4, + 2 + ] + } + } + }, + { + "origin": [ + -4, + 29, + -4.5 + ], + "size": [ + 2, + 1, + 0.1 + ], + "uv": { + "north": { + "uv": [ + 40, + 10 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 42, + 11 + ], + "uv_size": [ + 4, + 2 + ] + } + } + }, + { + "origin": [ + 2, + 29, + -4.5 + ], + "size": [ + 2, + 1, + 0.1 + ], + "uv": { + "north": { + "uv": [ + 46, + 10 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 42, + 11 + ], + "uv_size": [ + 4, + 2 + ] + } + } + }, + { + "origin": [ + -5, + 32, + -2 + ], + "size": [ + 10, + 1, + 6 + ], + "uv": { + "east": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "west": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "up": { + "uv": [ + 48, + 7 + ], + "uv_size": [ + -8, + -6 + ] + } + } + }, + { + "origin": [ + -5, + 32, + -4 + ], + "size": [ + 10, + 1, + 2 + ], + "uv": { + "east": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "west": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "up": { + "uv": [ + 11, + 6 + ], + "uv_size": [ + -3, + -5 + ] + } + } + }, + { + "origin": [ + -5, + 25, + 4 + ], + "size": [ + 10, + 1, + 1 + ], + "pivot": [ + 5, + 25, + 4 + ], + "rotation": [ + -90, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "east": { + "uv": [ + 54, + 15 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "west": { + "uv": [ + 54, + 15 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 59, + 16 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -5, + 33, + 4 + ], + "size": [ + 10, + 1, + 1 + ], + "pivot": [ + 5, + 33, + 4 + ], + "rotation": [ + -90, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "east": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "west": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "up": { + "uv": [ + 61, + 9 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 4, + 32, + 4 + ], + "size": [ + 1, + 1, + 7 + ], + "pivot": [ + 5, + 32, + 4 + ], + "rotation": [ + -90, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "west": { + "uv": [ + 57, + 15 + ], + "uv_size": [ + 7, + 1 + ] + }, + "up": { + "uv": [ + 58, + 16 + ], + "uv_size": [ + -1, + -7 + ] + } + } + }, + { + "origin": [ + -5, + 32, + 4 + ], + "size": [ + 1, + 1, + 7 + ], + "pivot": [ + -4, + 32, + 4 + ], + "rotation": [ + -90, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 8, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "east": { + "uv": [ + 57, + 15 + ], + "uv_size": [ + 7, + 1 + ] + }, + "up": { + "uv": [ + 63, + 16 + ], + "uv_size": [ + -1, + -7 + ] + } + } + }, + { + "origin": [ + -4, + 32, + 4 + ], + "size": [ + 8, + 1, + 7 + ], + "pivot": [ + 3.5, + 32, + 4 + ], + "rotation": [ + -90, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 40, + 1 + ], + "uv_size": [ + 3, + 5 + ] + }, + "up": { + "uv": [ + 63, + 16 + ], + "uv_size": [ + -6, + -7 + ] + } + } + }, + { + "origin": [ + 2, + 24, + -4 + ], + "size": [ + 3, + 6, + 7 + ], + "uv": { + "west": { + "uv": [ + 49, + 10 + ], + "uv_size": [ + 6, + 6 + ] + }, + "down": { + "uv": [ + 54, + 14 + ], + "uv_size": [ + -5, + -4 + ] + } + } + }, + { + "origin": [ + -5, + 24, + -4 + ], + "size": [ + 3, + 6, + 7 + ], + "uv": { + "east": { + "uv": [ + 32, + 10 + ], + "uv_size": [ + 7, + 6 + ] + }, + "down": { + "uv": [ + 49, + 14 + ], + "uv_size": [ + 5, + -4 + ] + } + } + }, + { + "origin": [ + 4, + 30, + -4 + ], + "size": [ + 1, + 2, + 7 + ], + "uv": { + "west": { + "uv": [ + 49, + 8 + ], + "uv_size": [ + 6, + 2 + ] + } + } + }, + { + "origin": [ + -5, + 30, + -4 + ], + "size": [ + 1, + 2, + 7 + ], + "uv": { + "east": { + "uv": [ + 55, + 8 + ], + "uv_size": [ + -6, + 2 + ] + } + } + }, + { + "origin": [ + 2, + 24, + 3 + ], + "size": [ + 3, + 6, + 1 + ], + "uv": { + "south": { + "uv": [ + 0, + 0 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 54, + 10 + ], + "uv_size": [ + 1, + 6 + ] + }, + "down": { + "uv": [ + 46, + 16 + ], + "uv_size": [ + 3, + -1 + ] + } + } + }, + { + "origin": [ + -5, + 24, + 3 + ], + "size": [ + 3, + 6, + 1 + ], + "uv": { + "east": { + "uv": [ + 54, + 10 + ], + "uv_size": [ + 1, + 6 + ] + }, + "south": { + "uv": [ + 0, + 0 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 46, + 16 + ], + "uv_size": [ + 3, + -1 + ] + } + } + }, + { + "origin": [ + 4, + 30, + 3 + ], + "size": [ + 1, + 2, + 1 + ], + "uv": { + "west": { + "uv": [ + 54, + 10 + ], + "uv_size": [ + 1, + 2 + ] + } + } + }, + { + "origin": [ + -5, + 30, + 3 + ], + "size": [ + 1, + 2, + 1 + ], + "uv": { + "east": { + "uv": [ + 54, + 10 + ], + "uv_size": [ + 1, + 2 + ] + } + } + } + ] + }, + { + "name": "bipedBody", + "pivot": [ + 0, + 24, + 0 + ] + }, + { + "name": "armorBody", + "parent": "bipedBody", + "pivot": [ + 0, + 24, + 0 + ] + }, + { + "name": "bone", + "parent": "armorBody", + "pivot": [ + 0, + 21.3, + 3.1 + ] + }, + { + "name": "group", + "parent": "bone", + "pivot": [ + 0.10714, + 20.31429, + 3.4 + ], + "cubes": [ + { + "origin": [ + -0.35, + 20.5, + 2.3 + ], + "size": [ + 12.1, + 1.6, + 1.6 + ], + "inflate": 0.1, + "pivot": [ + 1.2, + 21.3, + 3.1 + ], + "rotation": [ + 0, + 0, + -22.5 + ], + "uv": { + "north": { + "uv": [ + 22, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "east": { + "uv": [ + 23, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 22, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 23, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 22, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "down": { + "uv": [ + 22, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 5.67, + 21.27, + 2.32 + ], + "size": [ + 7.56, + 1.56, + 1.56 + ], + "inflate": 0.06, + "pivot": [ + 7.2, + 21.3, + 3.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 25, + 43 + ], + "uv_size": [ + -4, + 2 + ] + }, + "east": { + "uv": [ + 21, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 22, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "down": { + "uv": [ + 21, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 10.155, + 23.505, + 2.305 + ], + "size": [ + 9.09, + 1.59, + 1.59 + ], + "inflate": 0.09, + "pivot": [ + 11.7, + 24.3, + 3.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 25, + 43 + ], + "uv_size": [ + -4, + 2 + ] + }, + "south": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 21, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "down": { + "uv": [ + 21, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 8.66, + 22.76, + 2.31 + ], + "size": [ + 9.08, + 1.58, + 1.58 + ], + "inflate": 0.08, + "pivot": [ + 10.2, + 22.8, + 3.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 25, + 43 + ], + "uv_size": [ + -4, + 2 + ] + }, + "east": { + "uv": [ + 21, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 22, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 21, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 2.68, + 19.78, + 2.33 + ], + "size": [ + 6.04, + 1.54, + 1.54 + ], + "inflate": 0.04, + "pivot": [ + 4.2, + 19.8, + 3.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 25, + 43 + ], + "uv_size": [ + -4, + 2 + ] + }, + "east": { + "uv": [ + 21, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 22, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "down": { + "uv": [ + 21, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 7.165, + 22.015, + 2.315 + ], + "size": [ + 7.57, + 1.57, + 1.57 + ], + "inflate": 0.07, + "pivot": [ + 8.7, + 21.3, + 3.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 25, + 43 + ], + "uv_size": [ + -4, + 2 + ] + }, + "east": { + "uv": [ + 20, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 22, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 21, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 4.175, + 20.525, + 2.325 + ], + "size": [ + 6.05, + 1.55, + 1.55 + ], + "inflate": 0.05, + "pivot": [ + 5.7, + 19.8, + 3.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 25, + 43 + ], + "uv_size": [ + -4, + 2 + ] + }, + "east": { + "uv": [ + 22, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 22, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "down": { + "uv": [ + 21, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + } + ] + }, + { + "name": "group2", + "parent": "bone", + "pivot": [ + -0.19286, + 20.31429, + 3.6 + ], + "rotation": [ + -180, + 0, + 180 + ], + "cubes": [ + { + "origin": [ + -0.65, + 20.5, + 3.3 + ], + "size": [ + 12.1, + 1.6, + 1.6 + ], + "inflate": 0.1, + "pivot": [ + 0.9, + 21.3, + 4.1 + ], + "rotation": [ + 0, + 0, + -22.5 + ], + "uv": { + "north": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "east": { + "uv": [ + 25, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 25, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "down": { + "uv": [ + 23, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 5.37, + 21.27, + 3.32 + ], + "size": [ + 7.56, + 1.56, + 1.56 + ], + "inflate": 0.06, + "pivot": [ + 6.9, + 21.3, + 4.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "east": { + "uv": [ + 23, + 23 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 21, + 24 + ], + "uv_size": [ + 5, + 1 + ] + }, + "down": { + "uv": [ + 22, + 25 + ], + "uv_size": [ + 5, + -1 + ] + } + } + }, + { + "origin": [ + 9.855, + 23.505, + 3.305 + ], + "size": [ + 9.09, + 1.59, + 1.59 + ], + "inflate": 0.09, + "pivot": [ + 11.4, + 24.3, + 4.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "south": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 26, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "down": { + "uv": [ + 23, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 8.36, + 22.76, + 3.31 + ], + "size": [ + 9.08, + 1.58, + 1.58 + ], + "inflate": 0.08, + "pivot": [ + 9.9, + 22.8, + 4.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "east": { + "uv": [ + 23, + 23 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 24, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 21, + 25 + ], + "uv_size": [ + 6, + -1 + ] + } + } + }, + { + "origin": [ + 2.38, + 19.78, + 3.33 + ], + "size": [ + 6.04, + 1.54, + 1.54 + ], + "inflate": 0.04, + "pivot": [ + 3.9, + 19.8, + 4.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "east": { + "uv": [ + 26, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 22, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "down": { + "uv": [ + 23, + 45 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 6.865, + 22.015, + 3.315 + ], + "size": [ + 7.57, + 1.57, + 1.57 + ], + "inflate": 0.07, + "pivot": [ + 8.4, + 21.3, + 4.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "east": { + "uv": [ + 23, + 23 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 24, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 21, + 25 + ], + "uv_size": [ + 5, + -1 + ] + } + } + }, + { + "origin": [ + 3.875, + 20.525, + 3.325 + ], + "size": [ + 6.05, + 1.55, + 1.55 + ], + "inflate": 0.05, + "pivot": [ + 5.4, + 19.8, + 4.1 + ], + "rotation": [ + 0, + 0, + 22.5 + ], + "uv": { + "north": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "east": { + "uv": [ + 23, + 25 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 23, + 43 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 25, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 22, + 24 + ], + "uv_size": [ + 4, + 1 + ] + }, + "down": { + "uv": [ + 22, + 25 + ], + "uv_size": [ + 4, + -1 + ] + } + } + } + ] + }, + { + "name": "bone3", + "parent": "armorBody", + "pivot": [ + 0, + 24, + 0 + ], + "cubes": [ + { + "origin": [ + -4, + 12, + -2 + ], + "size": [ + 8, + 12, + 4 + ], + "uv": { + "north": { + "uv": [ + 20, + 20 + ], + "uv_size": [ + 8, + 12 + ] + }, + "south": { + "uv": [ + 32, + 20 + ], + "uv_size": [ + 8, + 12 + ] + } + } + } + ] + }, + { + "name": "bone15", + "parent": "bone3", + "pivot": [ + 0, + 18, + -2.3 + ], + "cubes": [ + { + "origin": [ + -4.1, + 12, + -2.4 + ], + "size": [ + 8.2, + 3, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 20, + 45 + ], + "uv_size": [ + 8, + 3 + ] + }, + "east": { + "uv": [ + 20, + 45 + ], + "uv_size": [ + 1, + 3 + ] + }, + "west": { + "uv": [ + 27, + 45 + ], + "uv_size": [ + 1, + 3 + ] + }, + "up": { + "uv": [ + 20, + 38 + ], + "uv_size": [ + 8, + 1 + ] + }, + "down": { + "uv": [ + 20, + 47 + ], + "uv_size": [ + 8, + 1 + ] + } + } + }, + { + "origin": [ + -4.1, + 12, + 2.1 + ], + "size": [ + 8.2, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 32, + 47 + ], + "uv_size": [ + 8, + 1 + ] + }, + "east": { + "uv": [ + 32, + 47 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 32, + 47 + ], + "uv_size": [ + 8, + 1 + ] + }, + "west": { + "uv": [ + 32, + 47 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 40, + 48 + ], + "uv_size": [ + -8, + -1 + ] + }, + "down": { + "uv": [ + 40, + 48 + ], + "uv_size": [ + -8, + -1 + ] + } + } + }, + { + "origin": [ + 1, + 13, + 2.1 + ], + "size": [ + 3.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 46 + ], + "uv_size": [ + -3, + 1 + ] + }, + "east": { + "uv": [ + 34, + 46 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 32, + 46 + ], + "uv_size": [ + 3, + 1 + ] + }, + "west": { + "uv": [ + 32, + 47 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 35, + 47 + ], + "uv_size": [ + -3, + -1 + ] + } + } + }, + { + "origin": [ + -4.1, + 13, + 2.1 + ], + "size": [ + 3.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 32, + 46 + ], + "uv_size": [ + 3, + 1 + ] + }, + "east": { + "uv": [ + 32, + 47 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + 3, + 1 + ] + }, + "west": { + "uv": [ + 34, + 46 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 40, + 47 + ], + "uv_size": [ + -3, + -1 + ] + } + } + }, + { + "origin": [ + -1, + 14, + 2.1 + ], + "size": [ + 2.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -1, + 16, + 2.1 + ], + "size": [ + 2.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 1, + 17, + 2.1 + ], + "size": [ + 1.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -2, + 17, + 2.1 + ], + "size": [ + 1.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 1, + 19, + 2.1 + ], + "size": [ + 1.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 32, + 41 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 32, + 41 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 32, + 41 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 32, + 41 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 33, + 42 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -1, + 18, + 2.1 + ], + "size": [ + 2.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -4, + 20, + 2.1 + ], + "size": [ + 8.1, + 2, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 32, + 38 + ], + "uv_size": [ + 8, + 2 + ] + }, + "east": { + "uv": [ + 39, + 38 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 32, + 38 + ], + "uv_size": [ + 8, + 2 + ] + }, + "west": { + "uv": [ + 39, + 38 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 40, + 39 + ], + "uv_size": [ + -8, + -1 + ] + }, + "down": { + "uv": [ + 40, + 40 + ], + "uv_size": [ + -8, + -1 + ] + } + } + }, + { + "origin": [ + -2.1, + 23, + 2.1 + ], + "size": [ + 4.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 34, + 36 + ], + "uv_size": [ + 4, + 1 + ] + }, + "east": { + "uv": [ + 39, + 38 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 34, + 36 + ], + "uv_size": [ + 4, + 1 + ] + }, + "west": { + "uv": [ + 39, + 38 + ], + "uv_size": [ + 1, + 2 + ] + } + } + }, + { + "origin": [ + -3.1, + 22, + 2.1 + ], + "size": [ + 6.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 33, + 37 + ], + "uv_size": [ + 6, + 1 + ] + }, + "east": { + "uv": [ + 39, + 38 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 33, + 37 + ], + "uv_size": [ + 6, + 1 + ] + }, + "west": { + "uv": [ + 39, + 38 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 39, + 38 + ], + "uv_size": [ + -6, + -1 + ] + } + } + }, + { + "origin": [ + -2, + 19, + 2.1 + ], + "size": [ + 2.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 36, + 40 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 37, + 40 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 36, + 40 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 36, + 40 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 38, + 41 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 3, + 18, + 2.1 + ], + "size": [ + 1.1, + 2, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 32, + 40 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 32, + 40 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 32, + 40 + ], + "uv_size": [ + 1, + 2 + ] + }, + "down": { + "uv": [ + 33, + 42 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -4, + 18, + 2.1 + ], + "size": [ + 1.1, + 2, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 39, + 40 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 39, + 40 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 39, + 40 + ], + "uv_size": [ + 1, + 2 + ] + }, + "down": { + "uv": [ + 40, + 42 + ], + "uv_size": [ + -1, + -2 + ] + } + } + }, + { + "origin": [ + -3, + 15, + 2.1 + ], + "size": [ + 2.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 38, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 37, + 44 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 39, + 45 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 1, + 15, + 2.1 + ], + "size": [ + 2.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 33, + 44 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 33, + 44 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 35, + 45 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 37, + 46 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -3.1, + 15, + -2.1 + ], + "size": [ + 6.2, + 2, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 6, + 2 + ] + }, + "east": { + "uv": [ + 26, + 43 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 26, + 43 + ], + "uv_size": [ + 1, + 2 + ] + } + } + }, + { + "origin": [ + -3.1, + 22, + -2.4 + ], + "size": [ + 6.2, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 21, + 37 + ], + "uv_size": [ + 6, + 1 + ] + }, + "east": { + "uv": [ + 21, + 37 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 21, + 37 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 27, + 38 + ], + "uv_size": [ + -6, + -1 + ] + } + } + }, + { + "origin": [ + 1, + 23, + -2.4 + ], + "size": [ + 1.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 22, + 36 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 22, + 36 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 22, + 36 + ], + "uv_size": [ + 1, + 1 + ] + } + } + }, + { + "origin": [ + 2, + 20, + -2.7 + ], + "size": [ + 1.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 26, + 39 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 26, + 39 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 26, + 39 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 27, + 40 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 27, + 40 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -2.1, + 23, + -2.4 + ], + "size": [ + 1.1, + 1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 22, + 36 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 22, + 36 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 22, + 36 + ], + "uv_size": [ + 1, + 1 + ] + } + } + }, + { + "origin": [ + -4.1, + 17, + -2.4 + ], + "size": [ + 8.2, + 5, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 20, + 38 + ], + "uv_size": [ + 8, + 5 + ] + }, + "east": { + "uv": [ + 20, + 38 + ], + "uv_size": [ + 1, + 5 + ] + }, + "west": { + "uv": [ + 27, + 38 + ], + "uv_size": [ + 1, + 5 + ] + }, + "up": { + "uv": [ + 20, + 38 + ], + "uv_size": [ + 8, + 1 + ] + }, + "down": { + "uv": [ + 20, + 47 + ], + "uv_size": [ + 8, + 1 + ] + } + } + } + ] + }, + { + "name": "bipedRightArm", + "pivot": [ + -4, + 22, + 0 + ] + }, + { + "name": "armorRightArm", + "parent": "bipedRightArm", + "pivot": [ + -4, + 22, + 0 + ] + }, + { + "name": "bone11", + "parent": "armorRightArm", + "pivot": [ + -4, + 22, + 0 + ], + "cubes": [ + { + "origin": [ + -8.1, + 11.9, + -2.1 + ], + "size": [ + 4.2, + 12.1, + 4.2 + ], + "uv": { + "north": { + "uv": [ + 44, + 20 + ], + "uv_size": [ + 4, + 12 + ] + }, + "east": { + "uv": [ + 40, + 20 + ], + "uv_size": [ + 4, + 12 + ] + }, + "south": { + "uv": [ + 52, + 20 + ], + "uv_size": [ + 4, + 12 + ] + }, + "west": { + "uv": [ + 48, + 20 + ], + "uv_size": [ + 4, + 12 + ] + }, + "up": { + "uv": [ + 44, + 20 + ], + "uv_size": [ + 4, + -4 + ] + } + } + } + ] + }, + { + "name": "armorRightOut", + "parent": "bone11", + "pivot": [ + -7.1, + 24.1, + 2 + ], + "cubes": [ + { + "origin": [ + -8.45, + 20, + -2.25 + ], + "size": [ + 0.35, + 3.1, + 4.5 + ], + "uv": { + "north": { + "uv": [ + 43, + 37 + ], + "uv_size": [ + 1, + 3 + ] + }, + "east": { + "uv": [ + 40, + 37 + ], + "uv_size": [ + 4, + 3 + ] + }, + "south": { + "uv": [ + 43, + 37 + ], + "uv_size": [ + 1, + 3 + ] + }, + "west": { + "uv": [ + 40, + 37 + ], + "uv_size": [ + 4, + 3 + ] + }, + "up": { + "uv": [ + 44, + 40 + ], + "uv_size": [ + -1, + -3 + ] + }, + "down": { + "uv": [ + 44, + 40 + ], + "uv_size": [ + -1, + -3 + ] + } + } + }, + { + "origin": [ + -8.45, + 17, + 1.15 + ], + "size": [ + 0.35, + 1, + 1.1 + ], + "uv": { + "north": { + "uv": [ + 40, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 40, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 40, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 41, + 43 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -3.95, + 16, + 1.15 + ], + "size": [ + 0.35, + 1, + 1.1 + ], + "uv": { + "east": { + "uv": [ + 51, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 51, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 51, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 52, + 44 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -8.45, + 23.1, + -1.15 + ], + "size": [ + 0.35, + 0.9, + 2.3 + ], + "uv": { + "north": { + "uv": [ + 41, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 41, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 41, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 43, + 37 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -7.25, + 23, + 2.55 + ], + "size": [ + 0.35, + 1.1, + 2.3 + ], + "pivot": [ + -7.25, + 23, + 2.45 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 53, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 53, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 53, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 55, + 37 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 55, + 37 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -8.35, + 20.05, + 2.55 + ], + "size": [ + 0.25, + 2.95, + 1.2 + ], + "pivot": [ + -8.35, + 22, + 2.45 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 53, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 53, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 55, + 37 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 55, + 37 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -8.35, + 16, + 2.55 + ], + "size": [ + 0.35, + 2, + 2.3 + ], + "pivot": [ + -8.35, + 17, + 2.45 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 54, + 42 + ], + "uv_size": [ + 2, + 2 + ] + }, + "east": { + "uv": [ + 54, + 42 + ], + "uv_size": [ + 2, + 2 + ] + }, + "up": { + "uv": [ + 56, + 44 + ], + "uv_size": [ + -2, + -2 + ] + }, + "down": { + "uv": [ + 56, + 44 + ], + "uv_size": [ + -2, + -2 + ] + } + } + }, + { + "origin": [ + -6.25, + 16, + 2.75 + ], + "size": [ + 0.35, + 1, + 2.1 + ], + "pivot": [ + -6.25, + 17, + 2.45 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 52, + 43 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 52, + 43 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 54, + 44 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 54, + 44 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + -8.25, + 11.9, + 2.55 + ], + "size": [ + 0.35, + 4.1, + 4.3 + ], + "pivot": [ + -8.25, + 11.9, + 2.45 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 61, + 60 + ], + "uv_size": [ + -1, + 4 + ] + }, + "east": { + "uv": [ + 64, + 60 + ], + "uv_size": [ + -4, + 4 + ] + }, + "up": { + "uv": [ + 61, + 60 + ], + "uv_size": [ + -1, + 4 + ] + }, + "down": { + "uv": [ + 61, + 53 + ], + "uv_size": [ + 2, + -1 + ] + } + } + }, + { + "origin": [ + -7.35, + 23.1, + -2.05 + ], + "size": [ + 0.35, + 0.9, + 2.3 + ], + "pivot": [ + -7.25, + 23, + -2.15 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 55, + 52 + ], + "uv_size": [ + -1, + 1 + ] + }, + "east": { + "uv": [ + 45, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 54, + 52 + ], + "uv_size": [ + -1, + 1 + ] + }, + "west": { + "uv": [ + 45, + 36 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 53, + 53 + ], + "uv_size": [ + 1, + -1 + ] + }, + "down": { + "uv": [ + 53, + 53 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -8.45, + 16, + -2.05 + ], + "size": [ + 0.35, + 1, + 3.4 + ], + "pivot": [ + -8.35, + 16, + -2.15 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 56, + 59 + ], + "uv_size": [ + -2, + 1 + ] + }, + "east": { + "uv": [ + 56, + 59 + ], + "uv_size": [ + -2, + 1 + ] + }, + "south": { + "uv": [ + 56, + 59 + ], + "uv_size": [ + -2, + 1 + ] + }, + "west": { + "uv": [ + 44, + 43 + ], + "uv_size": [ + 3, + 1 + ] + }, + "up": { + "uv": [ + 54, + 60 + ], + "uv_size": [ + 2, + -1 + ] + } + } + }, + { + "origin": [ + -8.45, + 21, + -2.05 + ], + "size": [ + 0.35, + 2.1, + 4.5 + ], + "pivot": [ + -8.35, + 22, + -2.15 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 55, + 52 + ], + "uv_size": [ + -1, + 1 + ] + }, + "east": { + "uv": [ + 48, + 37 + ], + "uv_size": [ + -4, + 2 + ] + }, + "south": { + "uv": [ + 54, + 52 + ], + "uv_size": [ + -1, + 1 + ] + }, + "west": { + "uv": [ + 44, + 37 + ], + "uv_size": [ + 4, + 2 + ] + }, + "up": { + "uv": [ + 53, + 53 + ], + "uv_size": [ + 1, + -1 + ] + }, + "down": { + "uv": [ + 53, + 53 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -8.45, + 20, + -2.05 + ], + "size": [ + 0.35, + 1, + 3.4 + ], + "pivot": [ + -8.35, + 21, + -2.15 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 55, + 52 + ], + "uv_size": [ + -1, + 1 + ] + }, + "east": { + "uv": [ + 52, + 53 + ], + "uv_size": [ + 4, + 2 + ] + }, + "south": { + "uv": [ + 54, + 52 + ], + "uv_size": [ + -1, + 1 + ] + }, + "west": { + "uv": [ + 44, + 39 + ], + "uv_size": [ + 3, + 1 + ] + }, + "down": { + "uv": [ + 53, + 53 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -8.45, + 11.9, + -2.05 + ], + "size": [ + 0.35, + 4.1, + 4.5 + ], + "pivot": [ + -8.35, + 15, + -2.15 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 56, + 60 + ], + "uv_size": [ + -1, + 4 + ] + }, + "east": { + "uv": [ + 52, + 60 + ], + "uv_size": [ + 4, + 4 + ] + }, + "south": { + "uv": [ + 53, + 60 + ], + "uv_size": [ + -1, + 4 + ] + }, + "west": { + "uv": [ + 44, + 44 + ], + "uv_size": [ + 4, + 4 + ] + }, + "up": { + "uv": [ + 53, + 53 + ], + "uv_size": [ + 1, + -1 + ] + }, + "down": { + "uv": [ + 53, + 53 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -8.45, + 11.9, + -2.25 + ], + "size": [ + 0.35, + 5.1, + 4.5 + ], + "uv": { + "north": { + "uv": [ + 43, + 43 + ], + "uv_size": [ + 1, + 5 + ] + }, + "east": { + "uv": [ + 40, + 43 + ], + "uv_size": [ + 4, + 5 + ] + }, + "south": { + "uv": [ + 40, + 43 + ], + "uv_size": [ + 1, + 5 + ] + }, + "west": { + "uv": [ + 56, + 59 + ], + "uv_size": [ + 4, + 5 + ] + }, + "up": { + "uv": [ + 56, + 60 + ], + "uv_size": [ + 3, + -1 + ] + }, + "down": { + "uv": [ + 55, + 64 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -3.95, + 11.9, + -2.25 + ], + "size": [ + 0.35, + 4.1, + 4.5 + ], + "uv": { + "north": { + "uv": [ + 49, + 59 + ], + "uv_size": [ + 4, + 5 + ] + }, + "east": { + "uv": [ + 52, + 44 + ], + "uv_size": [ + -4, + 4 + ] + }, + "south": { + "uv": [ + 49, + 59 + ], + "uv_size": [ + 4, + 5 + ] + }, + "west": { + "uv": [ + 48, + 44 + ], + "uv_size": [ + 4, + 4 + ] + }, + "up": { + "uv": [ + 56, + 60 + ], + "uv_size": [ + 3, + -1 + ] + } + } + }, + { + "origin": [ + -7.1, + 24, + -2.3 + ], + "size": [ + 2.2, + 0.2, + 4.5 + ], + "uv": { + "north": { + "uv": [ + 45, + 35 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 54, + 49 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 45, + 32 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 47, + 32 + ], + "uv_size": [ + -2, + 4 + ] + }, + "down": { + "uv": [ + 47, + 36 + ], + "uv_size": [ + -2, + -4 + ] + } + } + }, + { + "origin": [ + -8.2, + 11.9, + -2.2 + ], + "size": [ + 4.4, + 0.2, + 4.4 + ], + "uv": { + "north": { + "uv": [ + 55, + 48 + ], + "uv_size": [ + -2, + 1 + ] + }, + "south": { + "uv": [ + 53, + 48 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 52, + 36 + ], + "uv_size": [ + -4, + -4 + ] + }, + "down": { + "uv": [ + 52, + 36 + ], + "uv_size": [ + -4, + -4 + ] + } + } + }, + { + "origin": [ + -8.2, + 24, + -1.3 + ], + "size": [ + 1.1, + 0.2, + 2.5 + ], + "uv": { + "north": { + "uv": [ + 55, + 49 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 55, + 49 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 55, + 49 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 45, + 35 + ], + "uv_size": [ + -1, + -2 + ] + }, + "down": { + "uv": [ + 45, + 35 + ], + "uv_size": [ + -1, + -2 + ] + } + } + } + ] + }, + { + "name": "bipedLeftArm", + "pivot": [ + 4, + 22, + 0 + ] + }, + { + "name": "armorLeftArm", + "parent": "bipedLeftArm", + "pivot": [ + 4, + 22, + 0 + ] + }, + { + "name": "bone8", + "parent": "armorLeftArm", + "pivot": [ + -4, + 22, + 0 + ], + "cubes": [ + { + "origin": [ + 3.9, + 11.9, + -2.1 + ], + "size": [ + 4.2, + 12.2, + 4.2 + ], + "uv": { + "north": { + "uv": [ + 36, + 52 + ], + "uv_size": [ + 4, + 12 + ] + }, + "east": { + "uv": [ + 32, + 52 + ], + "uv_size": [ + 4, + 12 + ] + }, + "south": { + "uv": [ + 44, + 52 + ], + "uv_size": [ + 4, + 12 + ] + }, + "west": { + "uv": [ + 40, + 52 + ], + "uv_size": [ + 4, + 12 + ] + }, + "up": { + "uv": [ + 36, + 48 + ], + "uv_size": [ + 4, + 4 + ] + } + } + } + ] + }, + { + "name": "armorLeftArmOut", + "parent": "bone8", + "pivot": [ + 7.1, + 24.1, + 2 + ], + "cubes": [ + { + "origin": [ + 8.1, + 18, + -2.25 + ], + "size": [ + 0.35, + 5, + 4.5 + ], + "uv": { + "north": { + "uv": [ + 56, + 53 + ], + "uv_size": [ + 1, + 5 + ] + }, + "east": { + "uv": [ + 60, + 53 + ], + "uv_size": [ + -4, + 5 + ] + }, + "south": { + "uv": [ + 59, + 53 + ], + "uv_size": [ + 1, + 5 + ] + }, + "west": { + "uv": [ + 56, + 53 + ], + "uv_size": [ + 4, + 5 + ] + }, + "up": { + "uv": [ + 57, + 57 + ], + "uv_size": [ + -1, + -4 + ] + }, + "down": { + "uv": [ + 60, + 57 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + 8.1, + 17, + -1.25 + ], + "size": [ + 0.35, + 1, + 3.5 + ], + "uv": { + "north": { + "uv": [ + 58, + 58 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 59, + 58 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 57, + 58 + ], + "uv_size": [ + 3, + 1 + ] + } + } + }, + { + "origin": [ + 8.1, + 23, + -1.15 + ], + "size": [ + 0.35, + 1, + 2.3 + ], + "uv": { + "north": { + "uv": [ + 57, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 57, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 57, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 59, + 53 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 6.9, + 23, + 2.55 + ], + "size": [ + 0.35, + 1, + 2.3 + ], + "pivot": [ + 7.25, + 23, + 2.45 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 61, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 61, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 63, + 53 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 63, + 53 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 8.1, + 20.95, + 2.55 + ], + "size": [ + 0.25, + 2.05, + 1.2 + ], + "pivot": [ + 8.35, + 22, + 2.45 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 61, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 61, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 63, + 53 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 8, + 17.9, + 2.55 + ], + "size": [ + 0.35, + 1.1, + 1.2 + ], + "pivot": [ + 8.35, + 18, + 2.45 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 61, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 61, + 52 + ], + "uv_size": [ + 2, + 1 + ] + } + } + }, + { + "origin": [ + 4.7, + 17.9, + 2.55 + ], + "size": [ + 0.35, + 1.1, + 1.2 + ], + "pivot": [ + 5.05, + 18, + 2.45 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 63, + 59 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 63, + 59 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 64, + 60 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 64, + 60 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.7, + 19, + 2.65 + ], + "size": [ + 0.35, + 1, + 1.1 + ], + "pivot": [ + 5.05, + 19, + 2.45 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 63, + 59 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 63, + 59 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 64, + 60 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 8, + 19, + 2.55 + ], + "size": [ + 0.35, + 2, + 3.4 + ], + "pivot": [ + 8.35, + 19, + 2.45 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 60, + 55 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 60, + 55 + ], + "uv_size": [ + 3, + 2 + ] + }, + "up": { + "uv": [ + 64, + 60 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 63, + 56 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 8, + 16, + 2.55 + ], + "size": [ + 0.35, + 2, + 2.3 + ], + "pivot": [ + 8.35, + 17, + 2.45 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 61, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 61, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 63, + 53 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 63, + 53 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 7.9, + 11.9, + 2.55 + ], + "size": [ + 0.35, + 4.1, + 4.3 + ], + "pivot": [ + 8.25, + 11.9, + 2.45 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 60, + 60 + ], + "uv_size": [ + 1, + 4 + ] + }, + "west": { + "uv": [ + 60, + 60 + ], + "uv_size": [ + 4, + 4 + ] + }, + "up": { + "uv": [ + 60, + 60 + ], + "uv_size": [ + 1, + 4 + ] + }, + "down": { + "uv": [ + 63, + 53 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 7, + 23, + -2.05 + ], + "size": [ + 0.35, + 1, + 2.3 + ], + "pivot": [ + 7.25, + 23, + -2.15 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 54, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 53, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 53, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 53, + 52 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 54, + 53 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 54, + 53 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 8.1, + 16, + -2.05 + ], + "size": [ + 0.35, + 1, + 2.3 + ], + "pivot": [ + 8.35, + 16, + -2.15 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 54, + 59 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 54, + 59 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 54, + 59 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 54, + 59 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 56, + 60 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 4.9, + 16, + -1.95 + ], + "size": [ + 0.35, + 1, + 1.2 + ], + "pivot": [ + 5.15, + 16, + -2.15 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 54, + 59 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 54, + 59 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 54, + 59 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 54, + 59 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 56, + 60 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 56, + 60 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 8.1, + 21, + -2.05 + ], + "size": [ + 0.35, + 2, + 4.5 + ], + "pivot": [ + 8.35, + 22, + -2.15 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 54, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 52, + 53 + ], + "uv_size": [ + 4, + 2 + ] + }, + "south": { + "uv": [ + 53, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 56, + 53 + ], + "uv_size": [ + -4, + 2 + ] + }, + "up": { + "uv": [ + 54, + 53 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 54, + 53 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 8.1, + 20, + -2.05 + ], + "size": [ + 0.35, + 1, + 3.4 + ], + "pivot": [ + 8.35, + 21, + -2.15 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 54, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 53, + 55 + ], + "uv_size": [ + 3, + 1 + ] + }, + "south": { + "uv": [ + 53, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 56, + 53 + ], + "uv_size": [ + -4, + 2 + ] + } + } + }, + { + "origin": [ + 8.1, + 17.9, + -2.05 + ], + "size": [ + 0.35, + 2.1, + 4.5 + ], + "pivot": [ + 8.35, + 19, + -2.15 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 54, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 52, + 56 + ], + "uv_size": [ + 4, + 2 + ] + }, + "south": { + "uv": [ + 53, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 56, + 56 + ], + "uv_size": [ + -4, + 2 + ] + }, + "up": { + "uv": [ + 54, + 53 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 54, + 53 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 8.1, + 11.9, + -2.05 + ], + "size": [ + 0.35, + 4.1, + 4.5 + ], + "pivot": [ + 8.35, + 15, + -2.15 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 55, + 60 + ], + "uv_size": [ + 1, + 4 + ] + }, + "east": { + "uv": [ + 52, + 60 + ], + "uv_size": [ + 4, + 4 + ] + }, + "south": { + "uv": [ + 52, + 60 + ], + "uv_size": [ + 1, + 4 + ] + }, + "west": { + "uv": [ + 56, + 60 + ], + "uv_size": [ + -4, + 4 + ] + }, + "up": { + "uv": [ + 54, + 53 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 54, + 53 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 8.1, + 11.9, + -2.25 + ], + "size": [ + 0.35, + 5.1, + 4.5 + ], + "uv": { + "north": { + "uv": [ + 56, + 59 + ], + "uv_size": [ + 1, + 5 + ] + }, + "east": { + "uv": [ + 60, + 59 + ], + "uv_size": [ + -4, + 5 + ] + }, + "south": { + "uv": [ + 59, + 59 + ], + "uv_size": [ + 1, + 5 + ] + }, + "west": { + "uv": [ + 56, + 59 + ], + "uv_size": [ + 4, + 5 + ] + }, + "up": { + "uv": [ + 59, + 60 + ], + "uv_size": [ + -3, + -1 + ] + }, + "down": { + "uv": [ + 56, + 64 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 3.6, + 11.9, + -2.25 + ], + "size": [ + 0.35, + 5.1, + 4.5 + ], + "uv": { + "north": { + "uv": [ + 53, + 59 + ], + "uv_size": [ + -4, + 5 + ] + }, + "east": { + "uv": [ + 53, + 59 + ], + "uv_size": [ + -4, + 5 + ] + }, + "south": { + "uv": [ + 53, + 59 + ], + "uv_size": [ + -4, + 5 + ] + }, + "west": { + "uv": [ + 53, + 59 + ], + "uv_size": [ + -4, + 5 + ] + }, + "up": { + "uv": [ + 59, + 60 + ], + "uv_size": [ + -3, + -1 + ] + }, + "down": { + "uv": [ + 56, + 64 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 3.6, + 17.9, + -2.25 + ], + "size": [ + 0.35, + 2.1, + 4.5 + ], + "uv": { + "east": { + "uv": [ + 53, + 56 + ], + "uv_size": [ + -4, + 2 + ] + }, + "south": { + "uv": [ + 53, + 56 + ], + "uv_size": [ + -4, + 2 + ] + }, + "west": { + "uv": [ + 53, + 56 + ], + "uv_size": [ + -4, + 2 + ] + }, + "up": { + "uv": [ + 49, + 58 + ], + "uv_size": [ + 4, + -2 + ] + }, + "down": { + "uv": [ + 49, + 58 + ], + "uv_size": [ + 4, + -2 + ] + } + } + }, + { + "origin": [ + 4.9, + 24, + -2.3 + ], + "size": [ + 2.2, + 0.2, + 4.5 + ], + "uv": { + "north": { + "uv": [ + 53, + 48 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 55, + 48 + ], + "uv_size": [ + -2, + 1 + ] + }, + "west": { + "uv": [ + 55, + 49 + ], + "uv_size": [ + -1, + 2 + ] + }, + "up": { + "uv": [ + 53, + 52 + ], + "uv_size": [ + 2, + -4 + ] + }, + "down": { + "uv": [ + 53, + 52 + ], + "uv_size": [ + 2, + -4 + ] + } + } + }, + { + "origin": [ + 3.8, + 11.9, + -2.2 + ], + "size": [ + 4.4, + 0.2, + 4.4 + ], + "uv": { + "north": { + "uv": [ + 53, + 48 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 55, + 48 + ], + "uv_size": [ + -2, + 1 + ] + }, + "up": { + "uv": [ + 53, + 64 + ], + "uv_size": [ + 2, + -4 + ] + }, + "down": { + "uv": [ + 56, + 52 + ], + "uv_size": [ + 4, + -4 + ] + } + } + }, + { + "origin": [ + 7.1, + 24, + -1.3 + ], + "size": [ + 1.1, + 0.2, + 2.5 + ], + "uv": { + "north": { + "uv": [ + 56, + 49 + ], + "uv_size": [ + -1, + 2 + ] + }, + "south": { + "uv": [ + 56, + 49 + ], + "uv_size": [ + -1, + 2 + ] + }, + "west": { + "uv": [ + 56, + 49 + ], + "uv_size": [ + -1, + 2 + ] + }, + "up": { + "uv": [ + 55, + 51 + ], + "uv_size": [ + 1, + -2 + ] + }, + "down": { + "uv": [ + 55, + 51 + ], + "uv_size": [ + 1, + -2 + ] + } + } + } + ] + }, + { + "name": "launcher", + "parent": "bone8", + "pivot": [ + 6, + 23.75, + -0.75 + ], + "cubes": [ + { + "origin": [ + 6.5, + 22.95, + 0.5 + ], + "size": [ + 1, + 3.5, + 1 + ], + "pivot": [ + 7.5, + 22.95, + 0.25 + ], + "rotation": [ + -47.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6, + 24.2, + 0.25 + ], + "size": [ + 2, + 1, + 1.5 + ], + "pivot": [ + 7.5, + 22.95, + 0.25 + ], + "rotation": [ + -47.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6.5, + 24.8, + 3.35 + ], + "size": [ + 1, + 1.25, + 1.5 + ], + "inflate": 0.01, + "pivot": [ + 7.5, + 25.05, + 3.35 + ], + "rotation": [ + 55, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6.5, + 24.35, + 2.7 + ], + "size": [ + 1, + 1.25, + 1 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6.25, + 25.6, + 2.2 + ], + "size": [ + 1.5, + 1.25, + 1.75 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + } + ] + }, + { + "name": "bone4", + "parent": "launcher", + "pivot": [ + 7, + 27.25, + 2.7 + ], + "cubes": [ + { + "origin": [ + 7.5, + 26.45, + -1.3 + ], + "size": [ + 0.5, + 1.6, + 5 + ], + "inflate": 0.01, + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 7.5, + 27.9, + -1.05 + ], + "size": [ + 0.5, + 0.6, + 3.75 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6, + 27.9, + -1.05 + ], + "size": [ + 0.5, + 0.6, + 3.75 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 7.75, + 27.2, + -0.3 + ], + "size": [ + 0.5, + 0.85, + 3 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 5.75, + 27.2, + -0.3 + ], + "size": [ + 0.5, + 0.85, + 3 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 7.75, + 26.35, + -0.8 + ], + "size": [ + 0.5, + 0.85, + 3.5 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 5.75, + 26.35, + -0.8 + ], + "size": [ + 0.5, + 0.85, + 3.5 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 7.5, + 26.1, + -1.05 + ], + "size": [ + 0.5, + 0.35, + 3.75 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6.5, + 25.85, + -1.05 + ], + "size": [ + 1, + 0.35, + 3.75 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6.5, + 28.35, + -1.05 + ], + "size": [ + 1, + 0.35, + 3.75 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6.5, + 28.1, + 1.45 + ], + "size": [ + 1, + 0.6, + 1.75 + ], + "inflate": 0.1, + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6, + 26.1, + -1.05 + ], + "size": [ + 0.5, + 0.35, + 3.75 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6, + 26.45, + -1.3 + ], + "size": [ + 0.5, + 1.6, + 5 + ], + "inflate": 0.01, + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6.5, + 26.1, + -1.3 + ], + "size": [ + 1, + 0.65, + 5 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6.5, + 27.85, + -1.3 + ], + "size": [ + 1, + 0.65, + 5 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 6.5, + 26.6, + 3.7 + ], + "size": [ + 1, + 1.4, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 59, + 13 + ], + "uv_size": [ + -1, + -1 + ] + } + } + } + ] + }, + { + "name": "doomblade", + "parent": "bone8", + "pivot": [ + 8.2, + 14.8, + 0 + ], + "rotation": [ + 90, + 180, + 90 + ] + }, + { + "name": "group4", + "parent": "doomblade", + "pivot": [ + -16.2, + 16.65, + 16.5 + ], + "cubes": [ + { + "origin": [ + 8.95, + 14.05, + 0.375 + ], + "size": [ + 0.75, + 1.5, + 1.125 + ], + "inflate": 0.01, + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 9.7, + 14.05, + 0.375 + ], + "size": [ + 0.75, + 1.5, + 0.75 + ], + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 5.3, + 14.05, + 0.375 + ], + "size": [ + 0.45, + 1.5, + 0.875 + ], + "inflate": 0.01, + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.33, + 14.3, + 0.375 + ], + "size": [ + 0.95, + 1, + 0.375 + ], + "inflate": 0.01, + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 2.83, + 14.3, + -0.02 + ], + "size": [ + 2.45, + 1, + 0.375 + ], + "inflate": 0.01, + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 3.08, + 14.55, + 0.375 + ], + "size": [ + 2.2, + 0.5, + 0.375 + ], + "inflate": 0.01, + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 5.55, + 14.05, + 0.875 + ], + "size": [ + 0.7, + 1.5, + 0.625 + ], + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 5.55, + 14.05, + 0.5 + ], + "size": [ + 0.45, + 1.5, + 0.375 + ], + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 8.7, + 14.05, + 1 + ], + "size": [ + 0.45, + 1.5, + 0.375 + ], + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 8.55, + 14.05, + 0.375 + ], + "size": [ + 0.7, + 1.5, + 0.625 + ], + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 5.05, + 14.05, + 0 + ], + "size": [ + 5.25, + 1.5, + 0.375 + ], + "uv": { + "north": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 11, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 12, + 4 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 5.7, + 14.2, + 0.375 + ], + "size": [ + 3.25, + 1.2, + 0.9 + ], + "uv": { + "north": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 3, + 1 + ] + }, + "east": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 3, + 1 + ] + }, + "south": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 3, + 1 + ] + }, + "west": { + "uv": [ + 58, + 12 + ], + "uv_size": [ + 3, + 1 + ] + }, + "up": { + "uv": [ + 61, + 13 + ], + "uv_size": [ + -3, + -1 + ] + }, + "down": { + "uv": [ + 61, + 13 + ], + "uv_size": [ + -3, + -1 + ] + } + } + } + ] + }, + { + "name": "group5", + "parent": "doomblade", + "pivot": [ + -16.2, + 16.65, + 16.5 + ], + "cubes": [ + { + "origin": [ + 4, + 15.565, + -6.375 + ], + "size": [ + 0.15, + 0.015, + 0.375 + ], + "pivot": [ + -10.1, + 9.55, + 6.75 + ], + "rotation": [ + 0, + -22.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.225, + 15.565, + -6.375 + ], + "size": [ + 0.15, + 0.015, + 0.375 + ], + "pivot": [ + -9.875, + 9.55, + 6.75 + ], + "rotation": [ + 0, + -22.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.45, + 15.565, + -6.375 + ], + "size": [ + 0.15, + 0.015, + 0.375 + ], + "pivot": [ + -9.65, + 9.55, + 6.75 + ], + "rotation": [ + 0, + -22.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.675, + 15.565, + -6.375 + ], + "size": [ + 0.15, + 0.015, + 0.375 + ], + "pivot": [ + -9.425, + 9.55, + 6.75 + ], + "rotation": [ + 0, + -22.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + } + ] + }, + { + "name": "group6", + "parent": "doomblade", + "pivot": [ + -16.2, + 16.65, + 16.5 + ], + "cubes": [ + { + "origin": [ + 4, + 14.035, + -6.375 + ], + "size": [ + 0.15, + 0.015, + 0.375 + ], + "pivot": [ + -10.1, + 9.55, + 6.75 + ], + "rotation": [ + 0, + -22.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.225, + 14.035, + -6.375 + ], + "size": [ + 0.15, + 0.015, + 0.375 + ], + "pivot": [ + -9.875, + 9.55, + 6.75 + ], + "rotation": [ + 0, + -22.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.45, + 14.035, + -6.375 + ], + "size": [ + 0.15, + 0.015, + 0.375 + ], + "pivot": [ + -9.65, + 9.55, + 6.75 + ], + "rotation": [ + 0, + -22.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.675, + 14.035, + -6.375 + ], + "size": [ + 0.15, + 0.015, + 0.375 + ], + "pivot": [ + -9.425, + 9.55, + 6.75 + ], + "rotation": [ + 0, + -22.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 2, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 3, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + } + ] + }, + { + "name": "blade", + "parent": "doomblade", + "pivot": [ + 4.8, + 14.85, + 0.7 + ], + "cubes": [ + { + "origin": [ + 0.36392, + 14.7, + 0.45238 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 0.4223, + 14.55, + 0.72738 + ], + "rotation": [ + 0, + -27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 0.26662, + 14.7, + 0.9 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 0.26662, + 14.7, + 0.1 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 0.75396, + 14.7, + 0.69158 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + -0.18766, + 14.55, + 1.51658 + ], + "rotation": [ + 0, + 27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "up": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + }, + "down": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + } + } + }, + { + "origin": [ + 1.11392, + 14.7, + 0.45238 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 1.1723, + 14.55, + 0.72738 + ], + "rotation": [ + 0, + -27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 1.01662, + 14.7, + 0.9 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 1.05, + 14.75, + 1.15 + ], + "size": [ + 0.25, + 0.1, + 0.225 + ], + "pivot": [ + 1.25, + 14.75, + 1.25 + ], + "rotation": [ + 0, + -62.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 1.01662, + 14.7, + 0.1 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 1.50396, + 14.7, + 0.69158 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 0.56234, + 14.55, + 1.51658 + ], + "rotation": [ + 0, + 27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "up": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + }, + "down": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + } + } + }, + { + "origin": [ + 2.25396, + 14.7, + 0.69158 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 1.31234, + 14.55, + 1.51658 + ], + "rotation": [ + 0, + 27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "up": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + }, + "down": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + } + } + }, + { + "origin": [ + 1.76662, + 14.7, + 0.1 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 1.8, + 14.75, + 1.15 + ], + "size": [ + 0.25, + 0.1, + 0.225 + ], + "pivot": [ + 2, + 14.75, + 1.25 + ], + "rotation": [ + 0, + -62.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "up": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + }, + "down": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + } + } + }, + { + "origin": [ + 1.76662, + 14.7, + 0.9 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 1.86392, + 14.7, + 0.45238 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 1.9223, + 14.55, + 0.72738 + ], + "rotation": [ + 0, + -27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 3.00396, + 14.7, + 0.69158 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 2.06234, + 14.55, + 1.51658 + ], + "rotation": [ + 0, + 27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "up": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + }, + "down": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + } + } + }, + { + "origin": [ + 2.51662, + 14.7, + 0.1 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 2.55, + 14.75, + 1.15 + ], + "size": [ + 0.25, + 0.1, + 0.225 + ], + "pivot": [ + 2.75, + 14.75, + 1.25 + ], + "rotation": [ + 0, + -62.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "up": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + }, + "down": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + } + } + }, + { + "origin": [ + 2.51662, + 14.7, + 0.9 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 2.61392, + 14.7, + 0.45238 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 2.6723, + 14.55, + 0.72738 + ], + "rotation": [ + 0, + -27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 3.75396, + 14.7, + 0.69158 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 2.81234, + 14.55, + 1.51658 + ], + "rotation": [ + 0, + 27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "up": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + }, + "down": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + } + } + }, + { + "origin": [ + 3.26662, + 14.7, + 0.1 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 3.3, + 14.75, + 1.15 + ], + "size": [ + 0.25, + 0.1, + 0.225 + ], + "pivot": [ + 3.5, + 14.75, + 1.25 + ], + "rotation": [ + 0, + -62.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "up": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + }, + "down": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + } + } + }, + { + "origin": [ + 3.26662, + 14.7, + 0.9 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 3.36392, + 14.7, + 0.45238 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 3.4223, + 14.55, + 0.72738 + ], + "rotation": [ + 0, + -27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.50396, + 14.7, + 0.69158 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 3.56234, + 14.55, + 1.51658 + ], + "rotation": [ + 0, + 27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "up": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + }, + "down": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + } + } + }, + { + "origin": [ + 4.01662, + 14.7, + 0.1 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.05, + 14.75, + 1.15 + ], + "size": [ + 0.25, + 0.1, + 0.225 + ], + "pivot": [ + 4.25, + 14.75, + 1.25 + ], + "rotation": [ + 0, + -62.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "up": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + }, + "down": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + } + } + }, + { + "origin": [ + 4.01662, + 14.7, + 0.9 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.11392, + 14.7, + 0.45238 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 4.1723, + 14.55, + 0.72738 + ], + "rotation": [ + 0, + -27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 5.25396, + 14.7, + 0.69158 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 4.31234, + 14.55, + 1.51658 + ], + "rotation": [ + 0, + 27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.375, + 0.2 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 0.5, + 0.2 + ] + }, + "up": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + }, + "down": { + "uv": [ + 5.375, + 5.5 + ], + "uv_size": [ + -0.375, + -0.5 + ] + } + } + }, + { + "origin": [ + 4.76662, + 14.7, + 0.1 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.8, + 14.75, + 1.15 + ], + "size": [ + 0.25, + 0.1, + 0.225 + ], + "pivot": [ + 5, + 14.75, + 1.25 + ], + "rotation": [ + 0, + -62.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.25, + 0.1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 0.225, + 0.1 + ] + }, + "up": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + }, + "down": { + "uv": [ + 0.25, + 5.225 + ], + "uv_size": [ + -0.25, + -0.225 + ] + } + } + }, + { + "origin": [ + 4.76662, + 14.7, + 0.9 + ], + "size": [ + 0.375, + 0.2, + 0.25 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 4.86392, + 14.7, + 0.45238 + ], + "size": [ + 0.375, + 0.2, + 0.5 + ], + "pivot": [ + 4.9223, + 14.55, + 0.72738 + ], + "rotation": [ + 0, + -27.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 0.55, + 14.75, + 0.15 + ], + "size": [ + 4.75, + 0.1, + 0.975 + ], + "uv": { + "north": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 0, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 1, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -1.2, + 14.75, + 0.15 + ], + "size": [ + 1.75, + 0.1, + 0.725 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -2.00595, + 14.7, + 0.0951 + ], + "size": [ + 2.5, + 0.2, + 0.225 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -1.50595, + 14.7, + 0.3201 + ], + "size": [ + 2, + 0.2, + 0.225 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -0.23338, + 14.7, + 0.675 + ], + "size": [ + 0.75, + 0.2, + 0.475 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -1.00595, + 14.7, + 0.5451 + ], + "size": [ + 1.75, + 0.2, + 0.225 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -1.55597, + 14.7, + 1.07007 + ], + "size": [ + 1, + 0.2, + 0.475 + ], + "pivot": [ + 0.39403, + 0, + 0.42007 + ], + "rotation": [ + 0, + -25, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -1.73967, + 14.7, + 1.57457 + ], + "size": [ + 0.25, + 0.2, + 0.475 + ], + "pivot": [ + -0.03967, + 0, + 0.92457 + ], + "rotation": [ + 0, + -47.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -1.92377, + 14.7, + 1.71887 + ], + "size": [ + 0.25, + 0.2, + 0.225 + ], + "pivot": [ + -0.22377, + 0, + 0.81887 + ], + "rotation": [ + 0, + -50, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -0.88962, + 14.7, + 0.5842 + ], + "size": [ + 0.75, + 0.2, + 0.475 + ], + "pivot": [ + -0.79584, + 15, + 0.74701 + ], + "rotation": [ + 0, + -10, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + -0.35269, + 14.7, + 0.66088 + ], + "size": [ + 0.5, + 0.2, + 0.475 + ], + "pivot": [ + -0.25891, + 15, + 0.82369 + ], + "rotation": [ + 0, + -17.5, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 5, + 5 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 6, + 6 + ], + "uv_size": [ + -1, + -1 + ] + } + } + } + ] + }, + { + "name": "bipedRightLeg", + "pivot": [ + -2, + 12, + 0 + ] + }, + { + "name": "armorRightLeg", + "parent": "bipedRightLeg", + "pivot": [ + -2, + 12, + 0 + ] + }, + { + "name": "bone6", + "parent": "armorRightLeg", + "pivot": [ + -4, + 22, + 0 + ], + "cubes": [ + { + "origin": [ + -0.1, + 0.2, + -2.2 + ], + "size": [ + 4.4, + 11.8, + 4.4 + ], + "uv": { + "north": { + "uv": [ + 8, + 20 + ], + "uv_size": [ + -4, + 12 + ] + }, + "south": { + "uv": [ + 16, + 20 + ], + "uv_size": [ + -4, + 12 + ] + }, + "west": { + "uv": [ + 4, + 20 + ], + "uv_size": [ + -4, + 12 + ] + }, + "up": { + "uv": [ + 8, + 20 + ], + "uv_size": [ + -4, + -4 + ] + } + } + } + ] + }, + { + "name": "armorRightBoot", + "parent": "bipedRightLeg", + "pivot": [ + -2, + 12, + 0 + ] + }, + { + "name": "bone9", + "parent": "armorRightBoot", + "pivot": [ + -4, + 22, + 0 + ] + }, + { + "name": "bone14", + "parent": "bone9", + "pivot": [ + -4, + 22, + 0 + ], + "cubes": [ + { + "origin": [ + 0, + -0.3, + -2.3 + ], + "size": [ + 4.6, + 2.2, + 4.6 + ], + "uv": { + "north": { + "uv": [ + 4, + 52 + ], + "uv_size": [ + 4, + 12 + ] + }, + "south": { + "uv": [ + 12, + 52 + ], + "uv_size": [ + 4, + 12 + ] + }, + "west": { + "uv": [ + 8, + 52 + ], + "uv_size": [ + 4, + 12 + ] + }, + "up": { + "uv": [ + 12, + 20 + ], + "uv_size": [ + -4, + -4 + ] + }, + "down": { + "uv": [ + 12, + 20 + ], + "uv_size": [ + -4, + -4 + ] + } + } + }, + { + "origin": [ + 0, + 2.8, + -2.5 + ], + "size": [ + 4.6, + 4.1, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 4, + 57 + ], + "uv_size": [ + 4, + 4 + ] + }, + "south": { + "uv": [ + 4, + 57 + ], + "uv_size": [ + 4, + 4 + ] + }, + "west": { + "uv": [ + 5, + 57 + ], + "uv_size": [ + -1, + 4 + ] + }, + "up": { + "uv": [ + 8, + 58 + ], + "uv_size": [ + -4, + -1 + ] + }, + "down": { + "uv": [ + 4, + 61 + ], + "uv_size": [ + 4, + -1 + ] + } + } + }, + { + "origin": [ + 0, + 5.8, + 2.2 + ], + "size": [ + 4.6, + 1.1, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 12, + 57 + ], + "uv_size": [ + 4, + 1 + ] + }, + "south": { + "uv": [ + 12, + 57 + ], + "uv_size": [ + 4, + 1 + ] + }, + "west": { + "uv": [ + 12, + 57 + ], + "uv_size": [ + 4, + 1 + ] + }, + "up": { + "uv": [ + 16, + 58 + ], + "uv_size": [ + -4, + -1 + ] + }, + "down": { + "uv": [ + 16, + 58 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + 0, + 2.8, + 2 + ], + "size": [ + 4.6, + 1.1, + 0.5 + ], + "uv": { + "north": { + "uv": [ + 12, + 60 + ], + "uv_size": [ + 4, + 1 + ] + }, + "south": { + "uv": [ + 12, + 60 + ], + "uv_size": [ + 4, + 1 + ] + }, + "west": { + "uv": [ + 12, + 60 + ], + "uv_size": [ + 4, + 1 + ] + }, + "up": { + "uv": [ + 16, + 61 + ], + "uv_size": [ + -4, + -1 + ] + }, + "down": { + "uv": [ + 16, + 61 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + 0, + -0.2, + -2.5 + ], + "size": [ + 4.6, + 2.1, + 0.7 + ], + "uv": { + "north": { + "uv": [ + 4, + 62 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 1, + 62 + ], + "uv_size": [ + 4, + 2 + ] + }, + "up": { + "uv": [ + 8, + 63 + ], + "uv_size": [ + -4, + -1 + ] + }, + "down": { + "uv": [ + 8, + 64 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + 2.3, + 6.9, + -2.5 + ], + "size": [ + 2.3, + 2.1, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 6, + 55 + ], + "uv_size": [ + 2, + 2 + ] + }, + "east": { + "uv": [ + 6, + 55 + ], + "uv_size": [ + 2, + 2 + ] + }, + "south": { + "uv": [ + 6, + 55 + ], + "uv_size": [ + 2, + 2 + ] + }, + "west": { + "uv": [ + 6, + 55 + ], + "uv_size": [ + 2, + 2 + ] + }, + "up": { + "uv": [ + 8, + 57 + ], + "uv_size": [ + -2, + -2 + ] + } + } + }, + { + "origin": [ + 0, + 6.9, + 2.2 + ], + "size": [ + 3.5, + 2.1, + 0.3 + ], + "uv": { + "east": { + "uv": [ + 12, + 55 + ], + "uv_size": [ + 3, + 2 + ] + }, + "south": { + "uv": [ + 12, + 55 + ], + "uv_size": [ + 3, + 2 + ] + }, + "west": { + "uv": [ + 12, + 55 + ], + "uv_size": [ + 3, + 2 + ] + }, + "up": { + "uv": [ + 15, + 57 + ], + "uv_size": [ + -3, + -2 + ] + } + } + }, + { + "origin": [ + 0, + 9, + 2.2 + ], + "size": [ + 1.2, + 1, + 0.3 + ], + "uv": { + "east": { + "uv": [ + 12, + 55 + ], + "uv_size": [ + 3, + 2 + ] + }, + "south": { + "uv": [ + 12, + 55 + ], + "uv_size": [ + 3, + 2 + ] + }, + "west": { + "uv": [ + 12, + 55 + ], + "uv_size": [ + 3, + 2 + ] + }, + "up": { + "uv": [ + 15, + 57 + ], + "uv_size": [ + -3, + -2 + ] + } + } + }, + { + "origin": [ + 0, + 10.9, + -2.5 + ], + "size": [ + 1.2, + 1.1, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 4, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 4, + 52 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 5, + 53 + ], + "uv_size": [ + -1, + -1 + ] + }, + "down": { + "uv": [ + 5, + 53 + ], + "uv_size": [ + -1, + -1 + ] + } + } + }, + { + "origin": [ + 3.3, + 9, + -2.5 + ], + "size": [ + 1.3, + 2, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 7, + 53 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 7, + 53 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 7, + 53 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 7, + 53 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 8, + 55 + ], + "uv_size": [ + -1, + -2 + ] + } + } + }, + { + "origin": [ + 4.6, + -0.2, + 2 + ], + "size": [ + 4.6, + 2.1, + 0.5 + ], + "pivot": [ + 4.6, + -0.2, + 2.3 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "east": { + "uv": [ + 12, + 62 + ], + "uv_size": [ + 4, + 2 + ] + }, + "south": { + "uv": [ + 8, + 62 + ], + "uv_size": [ + 4, + 2 + ] + }, + "up": { + "uv": [ + 8, + 63 + ], + "uv_size": [ + -4, + -1 + ] + }, + "down": { + "uv": [ + 8, + 64 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + 4.6, + 2.8, + 2.2 + ], + "size": [ + 4.6, + 8.2, + 0.3 + ], + "pivot": [ + 4.6, + 2.8, + 2.3 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "north": { + "uv": [ + 12, + 53 + ], + "uv_size": [ + -4, + 8 + ] + }, + "east": { + "uv": [ + 11, + 53 + ], + "uv_size": [ + 1, + 8 + ] + }, + "south": { + "uv": [ + 8, + 53 + ], + "uv_size": [ + 4, + 8 + ] + }, + "up": { + "uv": [ + 12, + 54 + ], + "uv_size": [ + -4, + -1 + ] + }, + "down": { + "uv": [ + 11, + 61 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + -0.2, + 2.8, + 2.3 + ], + "size": [ + 4.6, + 4.1, + 0.3 + ], + "pivot": [ + -0.2, + 2.8, + 2.3 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "up": { + "uv": [ + 4, + 58 + ], + "uv_size": [ + -4, + -1 + ] + }, + "down": { + "uv": [ + 11, + 61 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + -0.2, + -0.2, + 2.3 + ], + "size": [ + 4.6, + 2.1, + 0.4 + ], + "pivot": [ + -0.2, + -0.2, + 2.3 + ], + "rotation": [ + 0, + 90, + 0 + ], + "uv": { + "up": { + "uv": [ + 8, + 63 + ], + "uv_size": [ + -4, + -1 + ] + }, + "down": { + "uv": [ + 8, + 64 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + 0, + -0.2, + 2.1 + ], + "size": [ + 4.6, + 2.1, + 0.4 + ], + "uv": { + "south": { + "uv": [ + 12, + 62 + ], + "uv_size": [ + 4, + 2 + ] + }, + "west": { + "uv": [ + 12, + 62 + ], + "uv_size": [ + 4, + 2 + ] + }, + "up": { + "uv": [ + 8, + 63 + ], + "uv_size": [ + -4, + -1 + ] + }, + "down": { + "uv": [ + 16, + 64 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + 1.1, + 1.9, + 2.2 + ], + "size": [ + 2.4, + 1, + 0.3 + ], + "uv": { + "east": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + } + } + }, + { + "origin": [ + 0, + 3.9, + 2.1 + ], + "size": [ + 1.2, + 1, + 0.4 + ], + "uv": { + "south": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 15, + 62 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 3.4, + 3.9, + 1.9 + ], + "size": [ + 1.2, + 1, + 0.6 + ], + "uv": { + "east": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 15, + 62 + ], + "uv_size": [ + -2, + -1 + ] + }, + "down": { + "uv": [ + 15, + 62 + ], + "uv_size": [ + -2, + -1 + ] + } + } + }, + { + "origin": [ + 1.1, + 1.9, + -2.5 + ], + "size": [ + 2.4, + 1, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 13, + 61 + ], + "uv_size": [ + 2, + 1 + ] + } + } + } + ] + }, + { + "name": "bipedLeftLeg", + "pivot": [ + 2, + 12, + 0 + ] + }, + { + "name": "armorLeftLeg", + "parent": "bipedLeftLeg", + "pivot": [ + 2, + 12, + 0 + ] + }, + { + "name": "bone7", + "parent": "armorLeftLeg", + "pivot": [ + -4, + 22, + 0 + ] + }, + { + "name": "armorLeftBoot", + "parent": "bipedLeftLeg", + "pivot": [ + 2, + 12, + 0 + ] + }, + { + "name": "bone10", + "parent": "armorLeftBoot", + "pivot": [ + -4, + 22, + 0 + ], + "cubes": [ + { + "origin": [ + -4.2, + -0.2, + -2.3 + ], + "size": [ + 4.6, + 12.2, + 4.6 + ], + "uv": { + "north": { + "uv": [ + 4, + 20 + ], + "uv_size": [ + 4, + 12 + ] + }, + "east": { + "uv": [ + 0, + 20 + ], + "uv_size": [ + 4, + 12 + ] + }, + "south": { + "uv": [ + 12, + 20 + ], + "uv_size": [ + 4, + 12 + ] + }, + "west": { + "uv": [ + 4, + 52 + ], + "uv_size": [ + -4, + 12 + ] + }, + "up": { + "uv": [ + 4, + 0 + ], + "uv_size": [ + 4, + 4 + ] + } + } + }, + { + "origin": [ + -4.2, + -0.3, + -2.3 + ], + "size": [ + 4.6, + 2.2, + 4.6 + ], + "uv": { + "north": { + "uv": [ + 8, + 52 + ], + "uv_size": [ + -4, + 12 + ] + }, + "east": { + "uv": [ + 12, + 52 + ], + "uv_size": [ + -4, + 12 + ] + }, + "south": { + "uv": [ + 16, + 52 + ], + "uv_size": [ + -4, + 12 + ] + }, + "down": { + "uv": [ + 8, + 20 + ], + "uv_size": [ + 4, + -4 + ] + } + } + }, + { + "origin": [ + -4.2, + 2.8, + -2.5 + ], + "size": [ + 4.6, + 4.1, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 8, + 57 + ], + "uv_size": [ + -4, + 4 + ] + }, + "east": { + "uv": [ + 4, + 57 + ], + "uv_size": [ + 1, + 4 + ] + }, + "up": { + "uv": [ + 4, + 58 + ], + "uv_size": [ + 4, + -1 + ] + }, + "down": { + "uv": [ + 8, + 61 + ], + "uv_size": [ + -4, + -1 + ] + } + } + }, + { + "origin": [ + -4.2, + 5.8, + 2.2 + ], + "size": [ + 4.6, + 1.1, + 0.3 + ], + "uv": { + "east": { + "uv": [ + 16, + 57 + ], + "uv_size": [ + -4, + 1 + ] + }, + "south": { + "uv": [ + 16, + 57 + ], + "uv_size": [ + -4, + 1 + ] + }, + "up": { + "uv": [ + 12, + 58 + ], + "uv_size": [ + 4, + -1 + ] + }, + "down": { + "uv": [ + 12, + 58 + ], + "uv_size": [ + 4, + -1 + ] + } + } + }, + { + "origin": [ + -4.2, + 2.8, + 2 + ], + "size": [ + 4.6, + 1.1, + 0.5 + ], + "uv": { + "east": { + "uv": [ + 16, + 60 + ], + "uv_size": [ + -4, + 1 + ] + }, + "south": { + "uv": [ + 16, + 60 + ], + "uv_size": [ + -4, + 1 + ] + }, + "up": { + "uv": [ + 12, + 61 + ], + "uv_size": [ + 4, + -1 + ] + }, + "down": { + "uv": [ + 12, + 61 + ], + "uv_size": [ + 4, + -1 + ] + } + } + }, + { + "origin": [ + -4.2, + -0.2, + -2.5 + ], + "size": [ + 4.6, + 2.1, + 0.7 + ], + "uv": { + "north": { + "uv": [ + 8, + 62 + ], + "uv_size": [ + -4, + 2 + ] + }, + "east": { + "uv": [ + 5, + 62 + ], + "uv_size": [ + -4, + 2 + ] + }, + "up": { + "uv": [ + 4, + 63 + ], + "uv_size": [ + 4, + -1 + ] + }, + "down": { + "uv": [ + 4, + 64 + ], + "uv_size": [ + 4, + -1 + ] + } + } + }, + { + "origin": [ + -4.2, + 6.9, + -2.5 + ], + "size": [ + 2.3, + 2.1, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 8, + 55 + ], + "uv_size": [ + -2, + 2 + ] + }, + "east": { + "uv": [ + 8, + 55 + ], + "uv_size": [ + -2, + 2 + ] + }, + "west": { + "uv": [ + 8, + 55 + ], + "uv_size": [ + -2, + 2 + ] + }, + "up": { + "uv": [ + 6, + 57 + ], + "uv_size": [ + 2, + -2 + ] + } + } + }, + { + "origin": [ + -4.2, + 6.9, + 2.2 + ], + "size": [ + 3.5, + 2.1, + 0.3 + ], + "uv": { + "east": { + "uv": [ + 15, + 55 + ], + "uv_size": [ + -3, + 2 + ] + }, + "south": { + "uv": [ + 15, + 55 + ], + "uv_size": [ + -3, + 2 + ] + }, + "west": { + "uv": [ + 15, + 55 + ], + "uv_size": [ + -3, + 2 + ] + }, + "up": { + "uv": [ + 12, + 57 + ], + "uv_size": [ + 3, + -2 + ] + } + } + }, + { + "origin": [ + -4.2, + 9, + 2.2 + ], + "size": [ + 1.2, + 1, + 0.3 + ], + "uv": { + "east": { + "uv": [ + 15, + 55 + ], + "uv_size": [ + -3, + 2 + ] + }, + "south": { + "uv": [ + 15, + 55 + ], + "uv_size": [ + -3, + 2 + ] + }, + "west": { + "uv": [ + 15, + 55 + ], + "uv_size": [ + -3, + 2 + ] + }, + "up": { + "uv": [ + 12, + 57 + ], + "uv_size": [ + 3, + -2 + ] + } + } + }, + { + "origin": [ + -0.8, + 10.9, + -2.5 + ], + "size": [ + 1.2, + 1.1, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 5, + 52 + ], + "uv_size": [ + -1, + 1 + ] + }, + "east": { + "uv": [ + 5, + 52 + ], + "uv_size": [ + -1, + 1 + ] + }, + "up": { + "uv": [ + 4, + 53 + ], + "uv_size": [ + 1, + -1 + ] + }, + "down": { + "uv": [ + 4, + 53 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -4.2, + 9, + -2.5 + ], + "size": [ + 1.3, + 2, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 8, + 53 + ], + "uv_size": [ + -1, + 2 + ] + }, + "east": { + "uv": [ + 8, + 53 + ], + "uv_size": [ + -1, + 2 + ] + }, + "west": { + "uv": [ + 8, + 53 + ], + "uv_size": [ + -1, + 2 + ] + }, + "up": { + "uv": [ + 7, + 55 + ], + "uv_size": [ + 1, + -2 + ] + } + } + }, + { + "origin": [ + -8.8, + -0.2, + 2 + ], + "size": [ + 4.6, + 2.1, + 0.5 + ], + "pivot": [ + -4.2, + -0.2, + 2.3 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "south": { + "uv": [ + 12, + 62 + ], + "uv_size": [ + -4, + 2 + ] + }, + "west": { + "uv": [ + 16, + 62 + ], + "uv_size": [ + -4, + 2 + ] + }, + "up": { + "uv": [ + 4, + 63 + ], + "uv_size": [ + 4, + -1 + ] + }, + "down": { + "uv": [ + 4, + 64 + ], + "uv_size": [ + 4, + -1 + ] + } + } + }, + { + "origin": [ + -8.8, + 2.8, + 2.2 + ], + "size": [ + 4.6, + 8.2, + 0.3 + ], + "pivot": [ + -4.2, + 2.8, + 2.3 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 8, + 53 + ], + "uv_size": [ + 4, + 8 + ] + }, + "south": { + "uv": [ + 12, + 53 + ], + "uv_size": [ + -4, + 8 + ] + }, + "west": { + "uv": [ + 12, + 53 + ], + "uv_size": [ + -1, + 8 + ] + }, + "up": { + "uv": [ + 8, + 54 + ], + "uv_size": [ + 4, + -1 + ] + }, + "down": { + "uv": [ + 7, + 61 + ], + "uv_size": [ + 4, + -1 + ] + } + } + }, + { + "origin": [ + -4, + 2.8, + 2.3 + ], + "size": [ + 4.6, + 4.1, + 0.3 + ], + "pivot": [ + 0.6, + 2.8, + 2.3 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "north": { + "uv": [ + 4, + 57 + ], + "uv_size": [ + -4, + 4 + ] + }, + "west": { + "uv": [ + 4, + 57 + ], + "uv_size": [ + -4, + 4 + ] + }, + "up": { + "uv": [ + 0, + 58 + ], + "uv_size": [ + 4, + -1 + ] + }, + "down": { + "uv": [ + 7, + 61 + ], + "uv_size": [ + 4, + -1 + ] + } + } + }, + { + "origin": [ + -4, + -0.2, + 2.3 + ], + "size": [ + 4.6, + 2.1, + 0.4 + ], + "pivot": [ + 0.6, + -0.2, + 2.3 + ], + "rotation": [ + 0, + -90, + 0 + ], + "uv": { + "up": { + "uv": [ + 4, + 63 + ], + "uv_size": [ + 4, + -1 + ] + }, + "down": { + "uv": [ + 4, + 64 + ], + "uv_size": [ + 4, + -1 + ] + } + } + }, + { + "origin": [ + -4.2, + -0.2, + 2.1 + ], + "size": [ + 4.6, + 2.1, + 0.4 + ], + "uv": { + "east": { + "uv": [ + 16, + 62 + ], + "uv_size": [ + -4, + 2 + ] + }, + "south": { + "uv": [ + 16, + 62 + ], + "uv_size": [ + -4, + 2 + ] + }, + "up": { + "uv": [ + 4, + 63 + ], + "uv_size": [ + 4, + -1 + ] + }, + "down": { + "uv": [ + 12, + 64 + ], + "uv_size": [ + 4, + -1 + ] + } + } + }, + { + "origin": [ + -3.1, + 1.9, + 2.2 + ], + "size": [ + 2.4, + 1, + 0.3 + ], + "uv": { + "east": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + }, + "south": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + }, + "west": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + } + } + }, + { + "origin": [ + -0.8, + 3.9, + 2.1 + ], + "size": [ + 1.2, + 1, + 0.4 + ], + "uv": { + "east": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + }, + "south": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + }, + "up": { + "uv": [ + 13, + 62 + ], + "uv_size": [ + 2, + -1 + ] + } + } + }, + { + "origin": [ + -4.2, + 3.9, + 1.9 + ], + "size": [ + 1.2, + 1, + 0.6 + ], + "uv": { + "east": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + }, + "south": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + }, + "west": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + }, + "up": { + "uv": [ + 13, + 62 + ], + "uv_size": [ + 2, + -1 + ] + } + } + }, + { + "origin": [ + -3.1, + 1.9, + -2.5 + ], + "size": [ + 2.4, + 1, + 0.3 + ], + "uv": { + "north": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + }, + "east": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + }, + "west": { + "uv": [ + 15, + 61 + ], + "uv_size": [ + -2, + 1 + ] + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/geo/item/pistol.geo.json b/common/src/main/resources/assets/azurelib/geo/item/pistol.geo.json new file mode 100644 index 000000000..dfef26e61 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/geo/item/pistol.geo.json @@ -0,0 +1,3690 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.unknown", + "texture_width": 128, + "texture_height": 64, + "visible_bounds_width": 3, + "visible_bounds_height": 2.5, + "visible_bounds_offset": [ + 0, + 0.75, + 0 + ] + }, + "bones": [ + { + "name": "group", + "pivot": [ + -0.49601, + 4.03502, + -0.09665 + ], + "cubes": [ + { + "origin": [ + -2, + -0.25, + -9.25 + ], + "size": [ + 3, + 4, + 7.75 + ], + "uv": { + "north": { + "uv": [ + 30, + 18 + ], + "uv_size": [ + 3, + 4 + ] + }, + "east": { + "uv": [ + 14, + 12 + ], + "uv_size": [ + 8, + 4 + ] + }, + "south": { + "uv": [ + 25, + 30 + ], + "uv_size": [ + 3, + 4 + ] + }, + "west": { + "uv": [ + 14, + 16 + ], + "uv_size": [ + 8, + 4 + ] + }, + "up": { + "uv": [ + 5, + 19 + ], + "uv_size": [ + 3, + 8 + ] + }, + "down": { + "uv": [ + 19, + 28 + ], + "uv_size": [ + 3, + -8 + ] + } + } + }, + { + "origin": [ + -1.375, + 1.25, + -11.75 + ], + "size": [ + 1.75, + 2.5, + 4.25 + ], + "uv": { + "north": { + "uv": [ + 37, + 6 + ], + "uv_size": [ + 2, + 3 + ] + }, + "east": { + "uv": [ + 30, + 26 + ], + "uv_size": [ + 4, + 3 + ] + }, + "south": { + "uv": [ + 16, + 37 + ], + "uv_size": [ + 2, + 3 + ] + }, + "west": { + "uv": [ + 28, + 30 + ], + "uv_size": [ + 4, + 3 + ] + }, + "up": { + "uv": [ + 17, + 29 + ], + "uv_size": [ + 2, + 4 + ] + }, + "down": { + "uv": [ + 3, + 40 + ], + "uv_size": [ + 2, + -4 + ] + } + } + }, + { + "origin": [ + -2.125, + 2, + -2.875 + ], + "size": [ + 3.25, + 1.5, + 2.75 + ], + "pivot": [ + -0.5, + 2.25, + -2.5 + ], + "rotation": [ + 45, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 27, + 4 + ], + "uv_size": [ + 3, + 2 + ] + }, + "east": { + "uv": [ + 37, + 18 + ], + "uv_size": [ + 3, + 2 + ] + }, + "south": { + "uv": [ + 37, + 20 + ], + "uv_size": [ + 3, + 2 + ] + }, + "west": { + "uv": [ + 37, + 22 + ], + "uv_size": [ + 3, + 2 + ] + }, + "up": { + "uv": [ + 32, + 33 + ], + "uv_size": [ + 3, + 3 + ] + }, + "down": { + "uv": [ + 34, + 9 + ], + "uv_size": [ + 3, + -3 + ] + } + } + }, + { + "origin": [ + -2.25, + 3.75, + -12 + ], + "size": [ + 3.5, + 3, + 18.5 + ], + "uv": { + "north": { + "uv": [ + 31, + 0 + ], + "uv_size": [ + 4, + 3 + ] + }, + "east": { + "uv": [ + 8, + 0 + ], + "uv_size": [ + 19, + 3 + ] + }, + "south": { + "uv": [ + 31, + 3 + ], + "uv_size": [ + 4, + 3 + ] + }, + "west": { + "uv": [ + 8, + 3 + ], + "uv_size": [ + 19, + 3 + ] + }, + "up": { + "uv": [ + 0, + 0 + ], + "uv_size": [ + 4, + 19 + ] + }, + "down": { + "uv": [ + 4, + 19 + ], + "uv_size": [ + 4, + -19 + ] + } + } + }, + { + "origin": [ + -1.875, + 4.75, + -11.875 + ], + "size": [ + 2.75, + 3, + 18.25 + ], + "uv": { + "north": { + "uv": [ + 14, + 34 + ], + "uv_size": [ + 3, + 3 + ] + }, + "east": { + "uv": [ + 8, + 6 + ], + "uv_size": [ + 18, + 3 + ] + }, + "south": { + "uv": [ + 34, + 21 + ], + "uv_size": [ + 3, + 3 + ] + }, + "west": { + "uv": [ + 8, + 9 + ], + "uv_size": [ + 18, + 3 + ] + }, + "up": { + "uv": [ + 8, + 12 + ], + "uv_size": [ + 3, + 18 + ] + }, + "down": { + "uv": [ + 11, + 30 + ], + "uv_size": [ + 3, + -18 + ] + } + } + }, + { + "origin": [ + -2.125, + -2.89866, + 3.40497 + ], + "size": [ + 3.25, + 4.5, + 3.5 + ], + "pivot": [ + -0.5, + 0.57009, + 6.21747 + ], + "rotation": [ + 22.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 5, + 27 + ], + "uv_size": [ + 3, + 5 + ] + }, + "east": { + "uv": [ + 22, + 12 + ], + "uv_size": [ + 4, + 5 + ] + }, + "south": { + "uv": [ + 19, + 28 + ], + "uv_size": [ + 3, + 5 + ] + }, + "west": { + "uv": [ + 22, + 17 + ], + "uv_size": [ + 4, + 5 + ] + }, + "up": { + "uv": [ + 31, + 6 + ], + "uv_size": [ + 3, + 4 + ] + }, + "down": { + "uv": [ + 31, + 26 + ], + "uv_size": [ + 3, + -4 + ] + } + } + }, + { + "origin": [ + -2, + -5.46979, + 4.98488 + ], + "size": [ + 3, + 3.125, + 3.125 + ], + "uv": { + "north": { + "uv": [ + 34, + 24 + ], + "uv_size": [ + 3, + 3 + ] + }, + "east": { + "uv": [ + 25, + 34 + ], + "uv_size": [ + 3, + 3 + ] + }, + "south": { + "uv": [ + 35, + 0 + ], + "uv_size": [ + 3, + 3 + ] + }, + "west": { + "uv": [ + 35, + 3 + ], + "uv_size": [ + 3, + 3 + ] + }, + "up": { + "uv": [ + 8, + 35 + ], + "uv_size": [ + 3, + 3 + ] + }, + "down": { + "uv": [ + 35, + 12 + ], + "uv_size": [ + 3, + -3 + ] + } + } + }, + { + "origin": [ + -2, + 0.47634, + 3.27997 + ], + "size": [ + 3, + 4.125, + 3.125 + ], + "uv": { + "north": { + "uv": [ + 0, + 32 + ], + "uv_size": [ + 3, + 4 + ] + }, + "east": { + "uv": [ + 3, + 32 + ], + "uv_size": [ + 3, + 4 + ] + }, + "south": { + "uv": [ + 32, + 29 + ], + "uv_size": [ + 3, + 4 + ] + }, + "west": { + "uv": [ + 17, + 33 + ], + "uv_size": [ + 3, + 4 + ] + }, + "up": { + "uv": [ + 11, + 35 + ], + "uv_size": [ + 3, + 3 + ] + }, + "down": { + "uv": [ + 35, + 15 + ], + "uv_size": [ + 3, + -3 + ] + } + } + }, + { + "origin": [ + -1.75, + -2.64866, + 2.65497 + ], + "size": [ + 2.5, + 4.75, + 4.75 + ], + "pivot": [ + -0.5, + 0.57009, + 6.21747 + ], + "rotation": [ + 22.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 14, + 29 + ], + "uv_size": [ + 3, + 5 + ] + }, + "east": { + "uv": [ + 0, + 19 + ], + "uv_size": [ + 5, + 5 + ] + }, + "south": { + "uv": [ + 8, + 30 + ], + "uv_size": [ + 3, + 5 + ] + }, + "west": { + "uv": [ + 14, + 20 + ], + "uv_size": [ + 5, + 5 + ] + }, + "up": { + "uv": [ + 11, + 30 + ], + "uv_size": [ + 3, + 5 + ] + }, + "down": { + "uv": [ + 22, + 35 + ], + "uv_size": [ + 3, + -5 + ] + } + } + }, + { + "origin": [ + -1.625, + -4.96979, + 4.23488 + ], + "size": [ + 2.25, + 3.125, + 4.25 + ], + "uv": { + "north": { + "uv": [ + 18, + 37 + ], + "uv_size": [ + 2, + 3 + ] + }, + "east": { + "uv": [ + 33, + 18 + ], + "uv_size": [ + 4, + 3 + ] + }, + "south": { + "uv": [ + 37, + 24 + ], + "uv_size": [ + 2, + 3 + ] + }, + "west": { + "uv": [ + 28, + 33 + ], + "uv_size": [ + 4, + 3 + ] + }, + "up": { + "uv": [ + 36, + 27 + ], + "uv_size": [ + 2, + 4 + ] + }, + "down": { + "uv": [ + 28, + 40 + ], + "uv_size": [ + 2, + -4 + ] + } + } + }, + { + "origin": [ + -1.625, + 0.60134, + 2.40497 + ], + "size": [ + 2.25, + 4, + 4.375 + ], + "uv": { + "north": { + "uv": [ + 30, + 36 + ], + "uv_size": [ + 2, + 4 + ] + }, + "east": { + "uv": [ + 26, + 18 + ], + "uv_size": [ + 4, + 4 + ] + }, + "south": { + "uv": [ + 36, + 31 + ], + "uv_size": [ + 2, + 4 + ] + }, + "west": { + "uv": [ + 22, + 26 + ], + "uv_size": [ + 4, + 4 + ] + }, + "up": { + "uv": [ + 32, + 36 + ], + "uv_size": [ + 2, + 4 + ] + }, + "down": { + "uv": [ + 34, + 40 + ], + "uv_size": [ + 2, + -4 + ] + } + } + }, + { + "origin": [ + -1.5, + 0.875, + -1.5 + ], + "size": [ + 2, + 0.5, + 6 + ], + "uv": { + "north": { + "uv": [ + 30, + 29 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 38, + 0 + ], + "uv_size": [ + 6, + 1 + ] + }, + "south": { + "uv": [ + 40, + 21 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 38, + 1 + ], + "uv_size": [ + 6, + 1 + ] + }, + "up": { + "uv": [ + 6, + 32 + ], + "uv_size": [ + 2, + 6 + ] + }, + "down": { + "uv": [ + 20, + 39 + ], + "uv_size": [ + 2, + -6 + ] + } + } + }, + { + "origin": [ + -2, + 3.875, + 4.5 + ], + "size": [ + 3, + 1, + 3 + ], + "uv": { + "north": { + "uv": [ + 39, + 38 + ], + "uv_size": [ + 3, + 1 + ] + }, + "east": { + "uv": [ + 40, + 4 + ], + "uv_size": [ + 3, + 1 + ] + }, + "south": { + "uv": [ + 40, + 10 + ], + "uv_size": [ + 3, + 1 + ] + }, + "west": { + "uv": [ + 40, + 11 + ], + "uv_size": [ + 3, + 1 + ] + }, + "up": { + "uv": [ + 35, + 15 + ], + "uv_size": [ + 3, + 3 + ] + }, + "down": { + "uv": [ + 22, + 38 + ], + "uv_size": [ + 3, + -3 + ] + } + } + }, + { + "origin": [ + -1.625, + 4, + 7.5 + ], + "size": [ + 2.25, + 0.75, + 2 + ], + "uv": { + "north": { + "uv": [ + 40, + 22 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 40, + 23 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 25, + 40 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 28, + 40 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 8, + 38 + ], + "uv_size": [ + 2, + 2 + ] + }, + "down": { + "uv": [ + 38, + 11 + ], + "uv_size": [ + 2, + -2 + ] + } + } + }, + { + "origin": [ + 1, + 0.625, + -7.5 + ], + "size": [ + 0.5, + 4.125, + 5 + ], + "uv": { + "north": { + "uv": [ + 4, + 28 + ], + "uv_size": [ + 1, + 4 + ] + }, + "east": { + "uv": [ + 22, + 22 + ], + "uv_size": [ + 5, + 4 + ] + }, + "south": { + "uv": [ + 10, + 38 + ], + "uv_size": [ + 1, + 4 + ] + }, + "west": { + "uv": [ + 0, + 24 + ], + "uv_size": [ + 5, + 4 + ] + }, + "up": { + "uv": [ + 5, + 36 + ], + "uv_size": [ + 1, + 5 + ] + }, + "down": { + "uv": [ + 27, + 42 + ], + "uv_size": [ + 1, + -5 + ] + } + } + }, + { + "origin": [ + -2.5, + 0.625, + -7.5 + ], + "size": [ + 0.5, + 4.125, + 5 + ], + "uv": { + "north": { + "uv": [ + 11, + 38 + ], + "uv_size": [ + 1, + 4 + ] + }, + "east": { + "uv": [ + 14, + 25 + ], + "uv_size": [ + 5, + 4 + ] + }, + "south": { + "uv": [ + 38, + 11 + ], + "uv_size": [ + 1, + 4 + ] + }, + "west": { + "uv": [ + 26, + 6 + ], + "uv_size": [ + 5, + 4 + ] + }, + "up": { + "uv": [ + 6, + 38 + ], + "uv_size": [ + 1, + 5 + ] + }, + "down": { + "uv": [ + 7, + 43 + ], + "uv_size": [ + 1, + -5 + ] + } + } + }, + { + "origin": [ + 1.25, + 4.625, + -10 + ], + "size": [ + 0.75, + 1.75, + 9 + ], + "uv": { + "north": { + "uv": [ + 30, + 4 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 26, + 10 + ], + "uv_size": [ + 9, + 2 + ] + }, + "south": { + "uv": [ + 34, + 27 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 26, + 12 + ], + "uv_size": [ + 9, + 2 + ] + }, + "up": { + "uv": [ + 35, + 27 + ], + "uv_size": [ + 1, + 9 + ] + }, + "down": { + "uv": [ + 0, + 45 + ], + "uv_size": [ + 1, + -9 + ] + } + } + }, + { + "origin": [ + -2.875, + 4.625, + -10 + ], + "size": [ + 0.625, + 1.75, + 9 + ], + "uv": { + "north": { + "uv": [ + 19, + 40 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 26, + 14 + ], + "uv_size": [ + 9, + 2 + ] + }, + "south": { + "uv": [ + 30, + 40 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 26, + 16 + ], + "uv_size": [ + 9, + 2 + ] + }, + "up": { + "uv": [ + 1, + 36 + ], + "uv_size": [ + 1, + 9 + ] + }, + "down": { + "uv": [ + 2, + 45 + ], + "uv_size": [ + 1, + -9 + ] + } + } + }, + { + "origin": [ + 0.375, + 2.625, + -11.5 + ], + "size": [ + 1.375, + 3.625, + 4 + ], + "uv": { + "north": { + "uv": [ + 12, + 38 + ], + "uv_size": [ + 1, + 4 + ] + }, + "east": { + "uv": [ + 26, + 26 + ], + "uv_size": [ + 4, + 4 + ] + }, + "south": { + "uv": [ + 13, + 38 + ], + "uv_size": [ + 1, + 4 + ] + }, + "west": { + "uv": [ + 27, + 0 + ], + "uv_size": [ + 4, + 4 + ] + }, + "up": { + "uv": [ + 22, + 38 + ], + "uv_size": [ + 1, + 4 + ] + }, + "down": { + "uv": [ + 23, + 42 + ], + "uv_size": [ + 1, + -4 + ] + } + } + }, + { + "origin": [ + -2.75, + 2.625, + -11.5 + ], + "size": [ + 1.375, + 3.625, + 4 + ], + "uv": { + "north": { + "uv": [ + 24, + 38 + ], + "uv_size": [ + 1, + 4 + ] + }, + "east": { + "uv": [ + 27, + 22 + ], + "uv_size": [ + 4, + 4 + ] + }, + "south": { + "uv": [ + 38, + 27 + ], + "uv_size": [ + 1, + 4 + ] + }, + "west": { + "uv": [ + 0, + 28 + ], + "uv_size": [ + 4, + 4 + ] + }, + "up": { + "uv": [ + 38, + 31 + ], + "uv_size": [ + 1, + 4 + ] + }, + "down": { + "uv": [ + 38, + 39 + ], + "uv_size": [ + 1, + -4 + ] + } + } + }, + { + "origin": [ + -3.5, + -0.125, + -5.875 + ], + "size": [ + 1.5, + 0.75, + 4 + ], + "uv": { + "north": { + "uv": [ + 31, + 40 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 38, + 5 + ], + "uv_size": [ + 4, + 1 + ] + }, + "south": { + "uv": [ + 33, + 40 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 38, + 15 + ], + "uv_size": [ + 4, + 1 + ] + }, + "up": { + "uv": [ + 36, + 35 + ], + "uv_size": [ + 2, + 4 + ] + }, + "down": { + "uv": [ + 14, + 41 + ], + "uv_size": [ + 2, + -4 + ] + } + } + }, + { + "origin": [ + -0.75, + -0.875, + -7.5 + ], + "size": [ + 0.5, + 0.625, + 4 + ], + "uv": { + "north": { + "uv": [ + 33, + 21 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 38, + 16 + ], + "uv_size": [ + 4, + 1 + ] + }, + "south": { + "uv": [ + 34, + 9 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 38, + 17 + ], + "uv_size": [ + 4, + 1 + ] + }, + "up": { + "uv": [ + 39, + 11 + ], + "uv_size": [ + 1, + 4 + ] + }, + "down": { + "uv": [ + 20, + 43 + ], + "uv_size": [ + 1, + -4 + ] + } + } + }, + { + "origin": [ + -1.25, + 1.125, + 6.5 + ], + "size": [ + 1.5, + 2.875, + 0.75 + ], + "pivot": [ + -0.5, + 2.5625, + 6.875 + ], + "rotation": [ + 22.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 25, + 37 + ], + "uv_size": [ + 2, + 3 + ] + }, + "east": { + "uv": [ + 21, + 39 + ], + "uv_size": [ + 1, + 3 + ] + }, + "south": { + "uv": [ + 38, + 2 + ], + "uv_size": [ + 2, + 3 + ] + }, + "west": { + "uv": [ + 40, + 12 + ], + "uv_size": [ + 1, + 3 + ] + }, + "up": { + "uv": [ + 40, + 39 + ], + "uv_size": [ + 2, + 1 + ] + }, + "down": { + "uv": [ + 40, + 41 + ], + "uv_size": [ + 2, + -1 + ] + } + } + }, + { + "origin": [ + -0.625, + 1.40248, + 1.84024 + ], + "size": [ + 0.25, + 2.875, + 0.625 + ], + "pivot": [ + -0.5, + 3.08998, + 2.02774 + ], + "rotation": [ + -45, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 16, + 40 + ], + "uv_size": [ + 1, + 3 + ] + }, + "east": { + "uv": [ + 17, + 40 + ], + "uv_size": [ + 1, + 3 + ] + }, + "south": { + "uv": [ + 18, + 40 + ], + "uv_size": [ + 1, + 3 + ] + }, + "west": { + "uv": [ + 40, + 18 + ], + "uv_size": [ + 1, + 3 + ] + }, + "up": { + "uv": [ + 39, + 8 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 42, + 24 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -0.75, + 2.52748, + 1.46524 + ], + "size": [ + 0.5, + 1.75, + 1 + ], + "pivot": [ + -0.5, + 3.08998, + 2.02774 + ], + "rotation": [ + -22.5, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 35, + 40 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 5, + 41 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 41, + 6 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 41, + 12 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 24, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 42, + 26 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -1.375, + 7.75, + 4.5 + ], + "size": [ + 1.75, + 0.375, + 1.875 + ], + "uv": { + "north": { + "uv": [ + 14, + 41 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 41, + 14 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 41, + 18 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 41, + 19 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 39, + 6 + ], + "uv_size": [ + 2, + 2 + ] + }, + "down": { + "uv": [ + 39, + 26 + ], + "uv_size": [ + 2, + -2 + ] + } + } + }, + { + "origin": [ + 0.125, + 8.125, + 4.5 + ], + "size": [ + 0.25, + 0.375, + 1.875 + ], + "uv": { + "north": { + "uv": [ + 42, + 26 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 41, + 20 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 27, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 41, + 24 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 25, + 41 + ], + "uv_size": [ + 1, + 2 + ] + }, + "down": { + "uv": [ + 41, + 27 + ], + "uv_size": [ + 1, + -2 + ] + } + } + }, + { + "origin": [ + 1.25, + 4.825, + 4 + ], + "size": [ + 0.25, + 0.875, + 1.5 + ], + "uv": { + "north": { + "uv": [ + 28, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 41, + 27 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 42, + 28 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 28, + 41 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 26, + 41 + ], + "uv_size": [ + 1, + 2 + ] + }, + "down": { + "uv": [ + 41, + 30 + ], + "uv_size": [ + 1, + -2 + ] + } + } + }, + { + "origin": [ + -2.5, + 4.825, + 4 + ], + "size": [ + 0.25, + 0.875, + 1.5 + ], + "uv": { + "north": { + "uv": [ + 29, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 41, + 30 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 42, + 29 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 31, + 41 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 41, + 31 + ], + "uv_size": [ + 1, + 2 + ] + }, + "down": { + "uv": [ + 33, + 43 + ], + "uv_size": [ + 1, + -2 + ] + } + } + }, + { + "origin": [ + 1.25, + 4.825, + 5.5 + ], + "size": [ + 0.625, + 0.875, + 0.875 + ], + "uv": { + "north": { + "uv": [ + 30, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 31, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 42, + 31 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 32, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 42, + 32 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 35, + 43 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -2.875, + 4.825, + 5.5 + ], + "size": [ + 0.625, + 0.875, + 0.875 + ], + "uv": { + "north": { + "uv": [ + 42, + 35 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 42, + 36 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 42, + 38 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 42, + 39 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 42, + 40 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 42, + 42 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + 0.125, + 8.5, + 5 + ], + "size": [ + 0.25, + 0.375, + 1.125 + ], + "uv": { + "north": { + "uv": [ + 42, + 42 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 43, + 2 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 43, + 3 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 43, + 4 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 5, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 43, + 6 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -1.375, + 8.125, + 4.5 + ], + "size": [ + 0.25, + 0.375, + 1.875 + ], + "uv": { + "north": { + "uv": [ + 6, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 41, + 33 + ], + "uv_size": [ + 2, + 1 + ] + }, + "south": { + "uv": [ + 43, + 6 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 41, + 34 + ], + "uv_size": [ + 2, + 1 + ] + }, + "up": { + "uv": [ + 34, + 41 + ], + "uv_size": [ + 1, + 2 + ] + }, + "down": { + "uv": [ + 41, + 37 + ], + "uv_size": [ + 1, + -2 + ] + } + } + }, + { + "origin": [ + -1.375, + 8.5, + 5 + ], + "size": [ + 0.25, + 0.375, + 1.125 + ], + "uv": { + "north": { + "uv": [ + 7, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 43, + 8 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 43, + 9 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 43, + 10 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 11, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 43, + 12 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -0.75, + 7.75, + -11 + ], + "size": [ + 0.5, + 0.375, + 1.375 + ], + "uv": { + "north": { + "uv": [ + 12, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 13, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 14, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 43, + 14 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 16, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 17, + 44 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + 1, + 3.375, + -14.625 + ], + "size": [ + 0.75, + 2, + 2 + ], + "pivot": [ + 13.5, + -5.25, + -14.5 + ], + "rotation": [ + -45, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 36, + 41 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 39, + 26 + ], + "uv_size": [ + 2, + 2 + ] + }, + "south": { + "uv": [ + 37, + 41 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 39, + 28 + ], + "uv_size": [ + 2, + 2 + ] + }, + "up": { + "uv": [ + 38, + 41 + ], + "uv_size": [ + 1, + 2 + ] + }, + "down": { + "uv": [ + 39, + 43 + ], + "uv_size": [ + 1, + -2 + ] + } + } + }, + { + "origin": [ + -2.75, + 3.375, + -14.625 + ], + "size": [ + 0.75, + 2, + 2 + ], + "pivot": [ + -13.5, + -5.25, + -14.5 + ], + "rotation": [ + -45, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 40, + 41 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 39, + 30 + ], + "uv_size": [ + 2, + 2 + ] + }, + "south": { + "uv": [ + 41, + 41 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 39, + 32 + ], + "uv_size": [ + 2, + 2 + ] + }, + "up": { + "uv": [ + 42, + 2 + ], + "uv_size": [ + 1, + 2 + ] + }, + "down": { + "uv": [ + 3, + 44 + ], + "uv_size": [ + 1, + -2 + ] + } + } + }, + { + "origin": [ + 1.75, + 3.875, + -14.125 + ], + "size": [ + 0.25, + 1, + 1 + ], + "pivot": [ + 13.5, + -5.25, + -14.5 + ], + "rotation": [ + -45, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 18, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 43, + 18 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 43, + 19 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 20, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 43, + 20 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 21, + 44 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -3, + 3.875, + -14.125 + ], + "size": [ + 0.25, + 1, + 1 + ], + "pivot": [ + -13.5, + -5.25, + -14.5 + ], + "rotation": [ + -45, + 0, + 0 + ], + "uv": { + "north": { + "uv": [ + 43, + 21 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 22, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 43, + 22 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 43, + 23 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 24, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 43, + 25 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -1.5, + 4.25, + -13.5 + ], + "size": [ + 2, + 2, + 1.5 + ], + "pivot": [ + -0.5, + 5.25, + -12.75 + ], + "rotation": [ + 0, + 0, + -45 + ], + "uv": { + "north": { + "uv": [ + 39, + 34 + ], + "uv_size": [ + 2, + 2 + ] + }, + "east": { + "uv": [ + 36, + 39 + ], + "uv_size": [ + 2, + 2 + ] + }, + "south": { + "uv": [ + 39, + 36 + ], + "uv_size": [ + 2, + 2 + ] + }, + "west": { + "uv": [ + 38, + 39 + ], + "uv_size": [ + 2, + 2 + ] + }, + "up": { + "uv": [ + 40, + 2 + ], + "uv_size": [ + 2, + 2 + ] + }, + "down": { + "uv": [ + 3, + 42 + ], + "uv_size": [ + 2, + -2 + ] + } + } + }, + { + "origin": [ + -1.5, + 5.25, + 6.375 + ], + "size": [ + 2, + 2, + 0.25 + ], + "pivot": [ + -0.5, + 6.25, + 7.5 + ], + "rotation": [ + 0, + 0, + 45 + ], + "uv": { + "north": { + "uv": [ + 8, + 40 + ], + "uv_size": [ + 2, + 2 + ] + }, + "east": { + "uv": [ + 4, + 42 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 40, + 8 + ], + "uv_size": [ + 2, + 2 + ] + }, + "west": { + "uv": [ + 42, + 5 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 41, + 37 + ], + "uv_size": [ + 2, + 1 + ] + }, + "down": { + "uv": [ + 42, + 8 + ], + "uv_size": [ + 2, + -1 + ] + } + } + }, + { + "origin": [ + -0.75, + 6, + 6.625 + ], + "size": [ + 0.5, + 0.5, + 0.125 + ], + "pivot": [ + -0.5, + 6.25, + 7.5 + ], + "rotation": [ + 0, + 0, + 45 + ], + "uv": { + "north": { + "uv": [ + 25, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "east": { + "uv": [ + 43, + 25 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 26, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "west": { + "uv": [ + 43, + 26 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 27, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 43, + 28 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + 0.25, + 5.375, + 6.625 + ], + "size": [ + 0.125, + 1.75, + 0.125 + ], + "pivot": [ + -0.5, + 6.25, + 7.5 + ], + "rotation": [ + 0, + 0, + 45 + ], + "uv": { + "north": { + "uv": [ + 8, + 42 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 42, + 8 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 9, + 42 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 10, + 42 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 28, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 43, + 29 + ], + "uv_size": [ + 1, + -1 + ] + } + } + }, + { + "origin": [ + -1.25, + 7, + 6.625 + ], + "size": [ + 1.5, + 0.125, + 0.125 + ], + "pivot": [ + -0.5, + 6.25, + 7.5 + ], + "rotation": [ + 0, + 0, + 45 + ], + "uv": { + "north": { + "uv": [ + 11, + 42 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 29, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 42, + 12 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 43, + 29 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 13, + 42 + ], + "uv_size": [ + 2, + 1 + ] + }, + "down": { + "uv": [ + 42, + 14 + ], + "uv_size": [ + 2, + -1 + ] + } + } + }, + { + "origin": [ + -1.25, + 5.375, + 6.625 + ], + "size": [ + 1.5, + 0.125, + 0.125 + ], + "pivot": [ + -0.5, + 6.25, + 7.5 + ], + "rotation": [ + 0, + 0, + 45 + ], + "uv": { + "north": { + "uv": [ + 42, + 15 + ], + "uv_size": [ + 2, + 1 + ] + }, + "east": { + "uv": [ + 30, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "south": { + "uv": [ + 42, + 16 + ], + "uv_size": [ + 2, + 1 + ] + }, + "west": { + "uv": [ + 43, + 30 + ], + "uv_size": [ + 1, + 1 + ] + }, + "up": { + "uv": [ + 42, + 17 + ], + "uv_size": [ + 2, + 1 + ] + }, + "down": { + "uv": [ + 21, + 43 + ], + "uv_size": [ + 2, + -1 + ] + } + } + }, + { + "origin": [ + -1.375, + 5.375, + 6.625 + ], + "size": [ + 0.125, + 1.75, + 0.125 + ], + "pivot": [ + -0.5, + 6.25, + 7.5 + ], + "rotation": [ + 0, + 0, + 45 + ], + "uv": { + "north": { + "uv": [ + 15, + 42 + ], + "uv_size": [ + 1, + 2 + ] + }, + "east": { + "uv": [ + 19, + 42 + ], + "uv_size": [ + 1, + 2 + ] + }, + "south": { + "uv": [ + 42, + 21 + ], + "uv_size": [ + 1, + 2 + ] + }, + "west": { + "uv": [ + 23, + 42 + ], + "uv_size": [ + 1, + 2 + ] + }, + "up": { + "uv": [ + 31, + 43 + ], + "uv_size": [ + 1, + 1 + ] + }, + "down": { + "uv": [ + 43, + 32 + ], + "uv_size": [ + 1, + -1 + ] + } + } + } + ] + }, + { + "name": "bone", + "parent": "group", + "pivot": [ + -0.49601, + 5.23502, + -13.29665 + ], + "cubes": [ + { + "origin": [ + -0.99601, + 4.73502, + -13.29665 + ], + "size": [ + 1, + 1, + 0 + ], + "uv": { + "north": { + "uv": [ + 128, + 0 + ], + "uv_size": [ + -64, + 64 + ] + }, + "east": { + "uv": [ + 128, + 0 + ], + "uv_size": [ + 0, + 1 + ] + }, + "south": { + "uv": [ + 64, + 0 + ], + "uv_size": [ + 64, + 64 + ] + }, + "west": { + "uv": [ + 128, + 0 + ], + "uv_size": [ + 0, + 1 + ] + }, + "up": { + "uv": [ + 128, + 1 + ], + "uv_size": [ + 0, + -1 + ] + }, + "down": { + "uv": [ + 128, + 1 + ], + "uv_size": [ + 0, + -1 + ] + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/models/block/stargate.json b/common/src/main/resources/assets/azurelib/models/block/stargate.json new file mode 100644 index 000000000..8997086ce --- /dev/null +++ b/common/src/main/resources/assets/azurelib/models/block/stargate.json @@ -0,0 +1,99 @@ +{ + "credit": "Made with Blockbench", + "parent": "builtin/entity", + "textures": { + "particle": "azurelib:block/stargate" + }, + "texture_size": [ + 256, + 256 + ], + "display": { + "thirdperson_righthand": { + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "thirdperson_lefthand": { + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "firstperson_righthand": { + "rotation": [ + 0, + -64, + 0 + ], + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "firstperson_lefthand": { + "rotation": [ + 0, + -64, + 0 + ], + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "ground": { + "translation": [ + 0, + 1.75, + 0 + ], + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "gui": { + "rotation": [ + 0, + -36, + 0 + ], + "translation": [ + 0, + -6, + 0 + ], + "scale": [ + 0.18, + 0.18, + 0.18 + ] + }, + "head": { + "scale": [ + 0, + 0, + 0 + ] + }, + "fixed": { + "translation": [ + 0, + -6, + 0 + ], + "scale": [ + 0.2, + 0.2, + 0.2 + ] + } + } +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/models/item/doomicorn_boots.json b/common/src/main/resources/assets/azurelib/models/item/doomicorn_boots.json new file mode 100644 index 000000000..befab1952 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/models/item/doomicorn_boots.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "azurelib:item/doomicorn_boots" + } +} diff --git a/common/src/main/resources/assets/azurelib/models/item/doomicorn_chestplate.json b/common/src/main/resources/assets/azurelib/models/item/doomicorn_chestplate.json new file mode 100644 index 000000000..96425b55a --- /dev/null +++ b/common/src/main/resources/assets/azurelib/models/item/doomicorn_chestplate.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "azurelib:item/doomicorn_chestplate" + } +} diff --git a/common/src/main/resources/assets/azurelib/models/item/doomicorn_helmet.json b/common/src/main/resources/assets/azurelib/models/item/doomicorn_helmet.json new file mode 100644 index 000000000..0229c662d --- /dev/null +++ b/common/src/main/resources/assets/azurelib/models/item/doomicorn_helmet.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "azurelib:item/doomicorn_helmet" + } +} diff --git a/common/src/main/resources/assets/azurelib/models/item/doomicorn_leggings.json b/common/src/main/resources/assets/azurelib/models/item/doomicorn_leggings.json new file mode 100644 index 000000000..1afbd089c --- /dev/null +++ b/common/src/main/resources/assets/azurelib/models/item/doomicorn_leggings.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "azurelib:item/doomicorn_leggings" + } +} diff --git a/common/src/main/resources/assets/azurelib/models/item/pistol.json b/common/src/main/resources/assets/azurelib/models/item/pistol.json new file mode 100644 index 000000000..dd331f022 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/models/item/pistol.json @@ -0,0 +1,96 @@ +{ + "credit": "Made with Blockbench", + "parent": "builtin/entity", + "texture_size": [ + 128, + 64 + ], + "display": { + "thirdperson_righthand": { + "translation": [ + 0, + -1.25, + -0.25 + ], + "scale": [ + 0.5, + 0.5, + 0.5 + ] + }, + "thirdperson_lefthand": { + "translation": [ + 0, + -1.25, + -0.25 + ], + "scale": [ + 0.5, + 0.5, + 0.5 + ] + }, + "firstperson_righthand": { + "translation": [ + 3, + -5.5, + -5.75 + ] + }, + "firstperson_lefthand": { + "translation": [ + 3, + -5.5, + -5.75 + ] + }, + "ground": { + "scale": [ + 0.5, + 0.5, + 0.5 + ] + }, + "gui": { + "rotation": [ + 40, + -38, + 0 + ], + "translation": [ + -1, + -1.25, + 0 + ], + "scale": [ + 0.7, + 0.7, + 0.7 + ] + }, + "head": { + "scale": [ + 0, + 0, + 0 + ] + }, + "fixed": { + "rotation": [ + 0, + -90, + 0 + ], + "translation": [ + -1.75, + -2.5, + -0.25 + ], + "scale": [ + 0.8, + 0.8, + 0.8 + ] + } + } +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/models/item/stargate.json b/common/src/main/resources/assets/azurelib/models/item/stargate.json new file mode 100644 index 000000000..bc6977543 --- /dev/null +++ b/common/src/main/resources/assets/azurelib/models/item/stargate.json @@ -0,0 +1,96 @@ +{ + "credit": "Made with Blockbench", + "parent": "builtin/entity", + "texture_size": [ + 256, + 256 + ], + "display": { + "thirdperson_righthand": { + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "thirdperson_lefthand": { + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "firstperson_righthand": { + "rotation": [ + 0, + -64, + 0 + ], + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "firstperson_lefthand": { + "rotation": [ + 0, + -64, + 0 + ], + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "ground": { + "translation": [ + 0, + 1.75, + 0 + ], + "scale": [ + 0.1, + 0.1, + 0.1 + ] + }, + "gui": { + "rotation": [ + 0, + -36, + 0 + ], + "translation": [ + 0, + -6, + 0 + ], + "scale": [ + 0.18, + 0.18, + 0.18 + ] + }, + "head": { + "scale": [ + 0, + 0, + 0 + ] + }, + "fixed": { + "translation": [ + 0, + -6, + 0 + ], + "scale": [ + 0.2, + 0.2, + 0.2 + ] + } + } +} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/textures/block/stargate.png b/common/src/main/resources/assets/azurelib/textures/block/stargate.png new file mode 100644 index 0000000000000000000000000000000000000000..7ef23d6292b596c40d528236854ba1b20c63a3da GIT binary patch literal 8667 zcmdsdc{tQ>`}SuT`<^MXCm{(TCQFP+ickoVo$O2Yb&TwxkZjqKLiX&kZ)F*UhVVrb zqb4&XTVr`|-|z2v-sgG$dH;L=cpZ+z%pCXUcHh^1Ue|S==RNTTH?>(9c^DxGV$r## zc?*JI;3W*Arv-m3d|o>qy?by=TLY>b<@*jnLXeIo$|x{*^||XKUfff{;pTE(%X<$) zpBvdvWm(L8YzMjTcCntAVtY>y%T1z-V-Fed{;Be~v(uKATZo(aSf}9cQ-c1SfsuVu zRDrUbp7u_w>M#Xm<*7~>ZI_UNqdRsg4Vh8L_-4xIoJ&BR&WZQIcUEJU&!@T7CfFNq z7!&BYO}3jQPn4S-Ds9g`7+q=_lHL`HW7vHWv6HeECYYs5TS@Hpvkc6mL$M3dQ@PDx z$ab$S5q)`hoz#hqI`^416}GZ6jnv{!QjmZOmJa&>8t5YPrQwk)F)*C>LeM=gzWMF- zsFcu4>sYdy-;*1A1-CW%Y?x8ZXL3fUAcR(=c!G=`=c+$%vjyw4+X+3rC_!7CynFHX zm>++tNSDkbb@?x=3X($_g$8IskO6`w#-`JoPdwohhY&B+xv_W3D!%VwB_juumZ8v) zEYvB(?8O%wdYLVcQ0Q!4tjajQy@+H5Gr&Q2`eD3{?Vu&~#YE(=Ij6>JnCXy@{XFCB_Xy|;TiOgw?g5_f9I$LmFNB@>E$A!KpO zFqzyU<V0ccO{(6red$LCbdZp4H7m` zkE;yCI~&}u)iUG!P7TS*W--g_^?dlih)m`UOxwZ-3D}#(;E|Dv>LPSOkfx-6KtNOk zZw$v?PV@55_*wTVlXAOvvBVZHKDuTLsYp4|mQ!r#OjnD6NRfnvAOVcJ6|VT`le7E8 zgMBZRl!nSH#>P#RT&_|FSyq=hT>>lvUayWbw;n+vUy2H-J*eiMHY1`J^ljaek%} z84>rB-w+TJf(hAjG@d93`R>Io8H--p9w?>ZK0_9Ae+;qL7$D9Bw(!Kj@(53`1Tcxb z-j%C{3(-@j@4%Y&>0UdS4La6!>CIX)b^36ao`-H|mQA`)rsJL?Y=&wz=7~Qh%T@J> zv4Y`KPG^U3RaOInvwJrzn!I2$Gc$urK21JLA1u`7v?Jl@IIUT8^y{2YwQtnzP4fmlj(ov)F+p-xV8^TezjG)<;!F66$w!(Rd`n*w0#b;fb~Y!}8B| z@|AJ#^C2g#`h$DSFIya!o3P$I~`3uhrB>k$;lD4rj^5 z1{l7guK-9v{xnC6Pvy>UsJxvyZ&YqdND^YGX{oY5AKa71Q|&j|K@Y4t<{^g z(W)i#F7B@$YO&ANwYA6SM*)!TVfp&(0-V;CD2Y4DdJl`l?Ow@Y&x!nlr9x*O=AKRx zNebjq=={K6e+gLNz^S5bxMi5B>&;nKCa6p^j(B@dsqP^hZ5p^mgg>Od0Be|? z66Aa=!oW9RgpO;DPfCUsHQoNz3X5k@l*)Cf@D3|GdfY59hJke7Ec-Ns+B|} z_L)We)GXkJYR2qkPZm7rX(fe!_lmfOPJIs#g3XoF*Gj&ab}RA;=EV7L3F#ag%^0e7 z=S{|U`dOx5KNG4=-M$fV`L(9x({3W|2?l`a!uT|{sSU(W#D2M;apj53Rp;w1KMk0Vpcn>| z*CUp)S@)r8C9fE!U!&Lv(^G-ePv{f=D}<~%+ZH6ZW?EFn%|-JIiZd0dkw$}h9HvG; zHQm6HjJLkT+Ub2|RmYdnqjBc7kwoC`(zESnFl=75`%ITpkvW8+Kjm>=z0^oaW&rD0 zS9k$(en z23Kb}z4*9N<0N}>!sTN6kb``VUSJ-VGA(*MEuYfgPaD2Zw8Gyw!+E&(*UTC$v>3MM zcRF@tWQ5HHmM)yvL}?s6E@JVQS*1C23=%%!*|G0m&1g_z5+&YI;>-?*g@jC6A4g9U zH-oXHPFgLu2DQMW%C-w6McPj@)cj3V-0o z6|zthUVLnVDs&Sd>;qjrr1s9)CM-;~@Er2)(Zju~^aw>-_Hu3OnmDV~%$@#bmZ^3o zRO;Gp8ifTFm>27uZN#L`ZqsW?6UR$!VC?tR9GYn`lz6(9{{zaSL+|Ib8GSgfm#)V6 zfm+D6%LHcX9ubh9PuR6SZk?%GssfeKWHKMcLdO?wOC+rX%eGeX&i*vq(xJfEEcHc+Mdhuef6`Q-0nWK6NxWvxH%OJL=74 z1?g=SwhRZFA#~5rmEm=<5o+s~U6}=f5D_t~a8T{u>V!{`R|o6BG=EJf5=wjjTjUSO z#XQXdl)oXut`GY9n;Mkww^L#)OWj9_;%mvKf+ocXR+uow*uYs(G1E6*ej5G*(>zX* zywM)PeKf|%n5ABOawK+ST{|AuJ8dCS1|COgfOt9HoK->Vc=OSPOj_rfDY0pi=ztl+;~780qBnR*&onRa2%ev z!fL18R@xzF^wF3%t}qan1*DX(75eY!*?z8vyltU?IlnvqIfy&07b@DK>8ycEjpbgU zQXkGW>pS}38np=2s~dlpiT#zJ`UTs6CK*m|3WB`1-Nr)_1Mbc`Ph#j#})UWIahkMm`RUtm85 zVXj>ef#6SUuR-*}ePa@x$G!pr7qU_q8;oh)CoZX}9DaMN@9Jze0SOIKo8G+d5X*i1 z|9A>SMl%64!Z-w2J^R_1Ur*R0 zAl310Aow)q8pk)-f1m%q_n%o>K_$<_veTZTD@u*?TWpa6+*yboRQv|_uI&pG0`-jQ z?7cg>dQk1OB&2SLh%2HWm;dO|_+)75@rdKc3HhB<{urTDCl{&$amLVyi1@!`nAcQN z0P|1A7dZ3CCQwQD9&`xojE~_TJX6yr9>On(-aZa0@u>u1K$azk?$?_Gl94KcLeQ9g z_EJW%zn7~QDj-vU!~pe9jT%jC0o1si%R`J_IH+V9_H82w6a%vi(EAFBN?^5RW%uQI zRJQ<}%`0MjnjbmVF<>`J{IYwwd-cc%i@Rc+%FoP>Delik{1ARNJN;;20&X1;pt#d$ z!`;=fy|slNl|Xg81IX*(-4r%F51}57PQZnvtad-#2_34np7CEmt+XVArYpck-cT~HO8_68 zc8UPt0zsDp^Qe)m?F7J-015nCn+3P&D(u^@zarwQ0J#5~4iD#j(W#Z88gr=^qSsi{ z$`D~t!2~$EcG#3a+IhW*_GfXw+^L(f7#5e$g^~ zXv59kw_g?5Iv3+$CXQfSQ<2WZC?!*C87ei4D?mUq0qvNK2ff}~pBVOynWb@Fs?f`q zy2N>OEkR_cTN4L%7d3iZtiZM0-gn-4zVIbT_ z%$I4-=M_Q~6%{n=qM}r~e6IC8$YiFxy>{}8kN2$|9B8V}X{mT`dpz_xA|N6N#!>52Jq0MQVdRS$letJNY`M-D3Ko%sFqqz13*7oSk=q z;rJ@Ro!1i_7K!;vtV@{*Y4gFRp7epQ$DIHWs zBA4`^pA1u$=fL?{b6+*MAu}wX_}modSG>FLq;*7mfMmrZcYc&g+CPo`he9vDQat~r zs!HtF^m1e=0~CT`z@Xg}m@ILNEu^va6xFRvwQDj6t&_-h3#n~--BKfsdh@`Sqix85d+8q zSwG-yZFZuGKE$FuVvDKMY=I10@F#TKetrmf3GUXWoUB?D>zaV8r;2dLzZL%p8N^# zR35eSZZk1uWeY)UAA{vC{SNZh5`9 zqB#Fn78E;D>k_NXMK*NUVP>LRw&TjDi6+bix6oSyIxc%}tqHo(%Z+i7ze5 zpxm9c^-xN-ewd{H1F4ggPm8I}pA?P%8vY4bEY>b*#zPI5`|@`g=tGn?nZRbeG};IM zCw^iyt^ShHlVh7ga><0Crb^(NqyQ_zgB2d1sb7*wC)Be&Q|+#7D#etOllVEy#j%Ov@EMShf2Am+`@1CC+p`(Il((IW#34&5x zR~O}(`_+41?97@mr`m27#r{>1lV;BStg^%p6L55xAt)S#prgQA4SUnrP3;sX zr%mjLd~a|5+cII!Fujyad{2#h6!#8~RLu%ZDC(5g17wv|y-@mg)y!yI+7DiWl4~}t zS;jgZ7amDbJFv-2Qpjunj!IuL>eDO9rt6$&Yo!fYiT7im#cn(qa{X60w6^sYDVKdM zwQEe7f6!!#S8pf-mxcKKuVJ`lhJ$#*F=zd<*??mbPZ_~d{+dI^&PdO7R=AQEBRO3u zHMulza&yy5gCM>E8kWmvbT9xTTrE~IfcA}5+vNU(5uL6)G^i-R?E;7!f1BsXolvBX z+q`0#VIRc)rXkOgtsLLYR#%rBk1TDUbN|*4=!_lk9@xrQ^Md?3Qh)8u1+2OJt)E_& z?J(K7P<1YQyfKKXZ{#s~sZeRn%=Wp~TPUfw1-B?uEx_Hkj24v7an?$b=QYEoU34z$C&|SsUileoNjj%_^Gw65+5|ai(9G`zE=JcBm87Wric?Fr7Sgm%J<85BP>ohvSO>g8)mFhW z0Zp9@xImXgmkl7aLw-(XB<0to1Z>2?Mx-5G{F#7*_t7_Dj$k^0;FVJmxn~5Izq(|f z2z~Eh#+vtIzsjkjYSIOzcxP*g3x4W81r9Y^c&t$BUImm-Vu&qWNfW9#a(D!GL+C1> zT4k#e{W2q^T~M=vH>p1o+jAWwZQ1f|J6E5Iz}{$qW2(8`O!m$Efw0aXeSKr4=82O# z3p^}fdQc_3naGK^$qe)Kl%C?dnkw?mr`cMfr$1l@6hX{_i3(FSRS#682Z6(=8dcsIKgyM=pX+Z8qFqN{RukbD*e3Prg5Qzj5ZiLXHdz zHAd(++?G&?biJ45a^25rR~mo6E{*ap#6d!xeu7 z`l^l$xNqn&JC;$6&A&))4j8t)`&+<&Zt!{ftz>+LLfxB=@AOJkBcD<~t{cTnXLY71 zSo_EoTOi(aLJZbghzJWI{APMM%?~G-+&7KdB<+5}aCTmK>e-gwg zNIYJBJ*rE0gnGrF3=B0${rwW~$x}jb?pF3&TU!|ERmnVIwLc$IH1_Y2_R?zf6SRJ| z;#SRWr%#T`x@A76xu(sZ4^O``HiQ_89C*NGrZ6S*tOB&*8uJ4q-%KAfWTekBsit>yHfLs1It$x|_$%2y8Z_YC2^hZydjd@J0dl&LyqI5m& z9!qT}`^DIhEDH@Fr66e7gZrx2?Zz2*K(I6l*+rnBJlwe1Yh^=`QP(sKA*k)}fC3C^ zie*S%%i#ID2fP1FOW}>3o<=x>X_wctxG%wnh7mKEL=-txp~DVDw7VQs>bh@oLrcQr z^1vD?a5lFc$W=sWG4K>rKhiUKUn+jRIBfRlX#_Pm4xstxv~(A0Ym|0$?&TmVi5dv4 zT%@K^2hVntI(wkolPX+@_RYG4vBIp zDQU97^~8BlqrX?it|M&vc*%Ysey}J_Its;97#y#=E!$1J4 z!}h3LmU?3|U;~E{7XQfeBz0;z>3c1A%T+mLTC&64+}t`hRP5`T_g#CvP4C`~0@n=T zdx1jM({2i;CvE4RjqM)DtPd@1tSOiF%zef2tGtzTT}X8ci28^!%b8<~HY8HDyo(Cj zqAl@n^etDyeK(036^t%*Z+u?!+BtvjcyeqOW7}(7?DED&7h(hI58&b&aUk9cw(~$L z=MM-*%pTtBCO6*4IkAuDVFxU4<`Q1*iVV9s|EM|Ko{y3~jokoZM|F#o6H5p>@+M;c z{1Hi>7gRH~v|K&Q5Wd{&)Zcdf1?P6Ku10R4jzSJgLc3|(!#*G89$*gwA&DQZnWZ*mB|U!_omBQp|Ien5prGZME}%73fBP8Ez?XoeV?FMWB1|IUgV_@YhwUpm z?zO8Cl*;lhf5(Z1h59OxGfi~7juCjDiKoQ+?NpeQH&IBfQM}^~?r}xj-}ZQ8U^=Pb z-V3?rRnukK$8H4+|MooV#m+wtBTQSP9z zQenCAxaMVES50nRzLU4))|qT&M^R{BQTtF(@yD{x-{7FWj$um^8C8@r6!s;I@(fz4j)Y+xC93ioEYK#41-S_Q_rt=T@H@saf&&eF`U&NCJN()c@Wn$1Ma6&~9V$ zEP!&glehQUT3Ql#6+IRbU3fb*sV!)COsBg7`~$p~hflU`j4BtB1uw=nN-}j&=E!T4 z^S9Tu5Bne9Fzk$K$ zk)6VKbytp=9&+vgEk;*+?S-ajKK8|=3k@ms5Sw(B`rNs5ftF{##;DuVJR6~N6Gs9kl{?= z6uZgnn>GE}bB-U~8sUiQRa1K!{bqT=5gz)r{Xw-PQ=jdHU;C|uwMJd8kcRQ^Vl`^9 zcZ`p32J?dyZTgiF>2z;$&H7`cbGG=iq0?dg4ySz>*#qvKxH>UXbIt#-G?DMmui>mY z@7&V#qW)bKL(+ygZ{pP;m57i`^;e~8ikr4(rxbWn57j{H$uaP!<##yn5?YS!bI zuJk-G=5W=FOn83*ogw6y=gD2C-=F{&9r&_ z5V_rFDi%Ifl@hY&%()$Ec1-UdTN3*Wdvt{r@@-<#IYi|}cYBoD%E40X*5S53RDI#E z^^8JVG{<*Xbc&6qg z!xPD`b%KA~e!nhWcF=Umx{A-T(O-6$NjasHQP*ul%k0Ng3hUF9Ov_Ql^0h!Bo3Xj| z3D_AfS<>$CpWvHmL6n&h&TlY2*?rY-%YFi(Q;Is-aHl8iTw45GODgeQWAmPuv-0bC zFhV7)f19NVuwp)+k#Uxwf4j{8?H9vQ?ElYyi{lU3N{}A*A1G$*5ctv2x~W;IVU77; DHE$o0 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/azurelib/textures/entity/marauder.png b/common/src/main/resources/assets/azurelib/textures/entity/marauder.png new file mode 100644 index 0000000000000000000000000000000000000000..0d1ba734d375a5c0079e4bbecc73cdc36ea2db8f GIT binary patch literal 11573 zcmYkCcRbba|Nmd-ICer-8PP&TC@bTfgG6OzWRFnxDA}BItV%Mxwv;BX)j+tzo(&! zs9a@@-wLF^MqDljggi;_0AOf_s4BpbvzButjhcO97t@jI=(g!Ce|YHSHT7dn@%lRJ zXR=mPM|rq^3vWQ*(~0y$@*k%D1uHmDnO&!b`>!cHA^nvj2~l2fn~#WE6JOh@Y{!4b zQ;jWiS!n*SCJvD7w2zQz<*7goKDwk}hiq`qS+_mt3RMD^*Nko->3NR6?0049QLb zKi*(7{0X}xm#(}!J*H%F?lTW`ywbHi;E=A|OfBGkz-v*udq( zaGo=<30#==Or}Rv|FY>W?&7Zajh?U58;%+ICEX9MIc6eN1F6jyZu0b&%jdHgXH}t_ zT5rt9lzNTF!w;D*%J2tHR6G#eI`SMMN2>OJ@c#@55}IRltL*tdw2m_J|JpAb++Om! zyRkQJ54kR&L7<-v8YNdhLW)7V`QcIp(YVH1rCEx!;9hLwXY9syIV$K)8$=pz>+wS$x{&?3$x%G- zGQnk^V#WwDgi5-=4F6qkD(f_TJ7%Y*CAx+f=d-KVx9= z%nNr+xKdkh?KiLuQjSJw0r^Z|b6vkFo;77-{6718}1kcAwx1Moo ziV}h}zgK4W_36L+G&IC;hig^0vDh>F($7WpZ8~NYWsf?0xZSp~_4*d7_N<=Z=+9v# zBW_!~3yZ-nh+uixTgnm$N&Ja`#N~&0+AWC0Xt9 z!=uH7O0-c#@kg>P!Fol+22QL>@ADnDD2ly+{?#F;U7zp-6>=4E6Gx2Nq1-!D(Nk3b z&yThw5>WGsWY+)lY#|_27%ru^kUit)OjQ7A{lWn5#-RdNHKD=l=EWr6@35nQpuE z$W+YSU7eT|5WS(^QFm}xn{;$&;t-YkoN`$3>A5cTru!&F)qP`mR?dk`4xW+XzcC(9 z-7|?2*mLMq4sq&OoA`ws7U=|V;g_YLs+ZYng&moLn4pFR)DKrE`CJeq!_Jm61nQI? z44-1addV02Yk%Ms#wwHF-(Hl{Q0yfw6145NGtOV}SEdinBa7HHXOBH)hwS`D@LI;5 zX@j}o%FCE?hH2*4wjp=<=(Q4mPGrqY2GFy`TT-q-YTM2yXBBC0`B(l~8GDjI+cd*_ zNkBvjC~NH+CgyC`9c+6QEs@&n&rHXuW9(kBi*nsVs#z92e$Q-~O|qXQE}7Kl5}(Wz4+M1q(^W@wA#I$R?`*OUNUC0CJJ(z zPr>EQhKLFpOsuXCwJwL2$4&swK}|Vbd^c+8DU|$X+4~@~OnjA567J^4(z4>P# zHa%zUMoLYw%H~R~(fM*2^)O=c4KGXI_=Gr}$`phO4qXX9YT$WlJ~6wV?G`hxz4bc@ z;@&J21>kThBiNOJ6_eE@AhS9n=*Uu{{&nnR8RVR~W5<7mq}&}5w~;G~Vj41tIHIxk zsfq zSa;L(`TJM<8-{S*1&5a8*gO3Tmm+R(&7AWMF*Dm=^VNY(p%!2Ra7dfxnc-f)2t;Vi zEN_&PVswqLZlibnbB~zyp(@;su2f5OYz32^^!4Hr|D748sf^@P@$*!=!n}8so0`9k zW_v1fholU@o-bVS=dvVkMJWMk%ge@QWN}9K@>8@V)NkeKxJo1jS=|N*3=oe z+f&O87#-386}DJjw&d%B<{N$YdkxL1O@gW6CIFbk!apD(!C-mzdt_ZD`+!Q=^%%-w z96}#;Nkt7a3sp{5odL_6cC`19+*vfQBn+1FjgwPT2KFsLQeMefsdD5C4E||94moZ2^QQ6a@8eZ((?73|HW{!jLZyg(9U}X=awqS%aIoA zM}H?^SFRRF`nyAc)s93iBjnwEMJ~qkTa=mMfK@A+f}Zp$y4}w2&FrKD=ZfaRLU_0b z+;K)x;#zAJo)n#B{_NcomZHR1gGMt0w0A!W2lqRh=%El3Tg5y7@W+*DDF#({&wlbv z)JGDt(Uxym&$vB%Mvvkn#*F|-j#!9o1QA9PfD?o@P!@MnB)p9zd5dI!KfZx0BBWVq8X zPu(~meoq7*bA6LFv)-Y;dlBjc39}9s284p83{L#tFnBe7rde2&~R?Zl{p8I=BNa{+%q{l9OYXi6HK6Z-9crnd$`$9DP#dL0NfIh;?m7HYF~*eP_sERIN)1I)<~%! zj8S?jKK%6@IPtjazIAhr5#{3nu_PV1-p0+3wg`mdiN%T7O&O}KhehA&h9Fml|18A;%33Y)_QRE z{L0>|VKK;$x0%U+!Ot$$Di}j{m;tX)CKFmzJp8-9-dU#K%&bAY8@&a4iNTXTzA97S z944Q&m<@jr+cGz1hXN-oi(9(Zs;1}i3DD=&A+~D&3XqOoKh^I9Cgrb2EF*6`E%FG9 zihggS%7LKtd5xj1{2VuWbv+-G`Ip}r0YLX_tXg%zHh5fr20}1{wA0l)P?PTTp5>F(gOmmDlNRq=tpLeeR1Wv-Kv2pq z9w>mK#h4l_yo@XQLdD*^#~dOhr0_gG?7MSBrXE{g8@@f2*v zhoI1Ml*k%3qY3epCeIXR#Z8w=-JKn=&Ek+81;}=TRB-ntZsa%m`qOMG%n;BN*9|EKD>Qj1 zYRTs5_I5jlh|uI$u|j8cg*cg#!k6I2|EG7}PXOwMBq02f`hS z)hb9GS@9$24F;oSHB$6&?pp3bk1#4D*(8C;$N=hCW^AiQ?UQ{?In>dz75Q+mK|avu zG|^f<%H@YR`L8u4w#f20q`<`0P%*lxbn%l>dse*sO?g{t?(Cw80%|ArJ4UahsuiC^hOBzFcIyoloYhBWGtAEX>i+f zyM2+jUuzv~MstcT7=0?&u^~<{yA1)Ga!r1J}4)_u9)UPxA!WZ0a}?j!B>-SYp5mhw;%oTS zcx~WazL_9D)Aq)0ZNcTK$U$uuaR0xn*)rAX7=x{*JB~AWR5)CKF!bxQ)Aj^aml;3| zo4!mK?VA`kjYh+DN;>}L^l?n&>c+MIO8F-l0DG^C`le1BJVM&hHkB(;er{=Z z=IR)+iR~x=sE?GJSqBSrRfhmSz69{@Y#G~g>*K0r4Z4q{PBPQID=jJ3ZW!HqQFD}O z@4t!)rUW7O@(I#eq=|Q^dwU1;#bkROVow0|(quWgg6BrJN2L^N=je*Y!S=;3m(yZ2 zW(|y#TP~~XaveH12{f;4s4=b^2o68L8f~=iC!!)(o#El)RQbv%*&*zoa+3B6&M8;& zRuuG-1BSQLxS0y{d{-)S(-k=^_Uqgv)PK|uEn>y%QOE%G=Gn1LrR7mh+B7#>ti+W)MaCC*;OKqBNUW#f_13*-&h%+6-?r40mD7yud%qG&+v|` zsF9tOZNBnqVYzD~7?ir*Gv__#wh7&i-t$P7jj;2* zHxduBXD4|ff1r5gw-GNJfN9>E{3N_}H7gd2ot3G>7(*{=C7s|fBKm|!Wq7VSkZg^7Z7!!t@Xf*oaU5OZUCtke8ESF z+KRbiXjGJL4!oTEZFjrrZM$|yY_tdu{gZ5y-sK!RH=RYnCNTV@AD)eDzkUta&uYCa z|79cs0VKSaep;16Ma{s2P>qpYnFFbdpF{i4$NxP-a;P;LI76kn)UdfAfuVOqKeU1x9s-YXLVhMUL`H z=wrPo_|5ONP$EF9!giPU+MLmZ)*8!BbROgIF%3Fza0G7a#K~AXx)&NW<)@DyXEyfr z2Z3p4_k0!}N1y~gvX%pZ(=k1J5wn{^D|z_7ilcs+06NSxM=CsP?W(LR4<`Qsq6+3$ zw5m~kupL71k(^09); zZ81uGV8K{v;IgE)87*Q?KKW7DB3{ZQCLQtgvB?T+FKJ$}`sl)1`@&kVQ=QM`5q&Hy zg?Hui`*viv@S$rM4XePKItXBPdT{lRI~aQH3Uo|d|Cf_~C!jPecdEZzM^C@_4V|8( z8g412$(@R}S;G=S*N^Zp6Pt&^UdoKihBgqkQH9OzV~aoS7|YYIq4uBS;sCm@*4C&)>E8zzp9142`@CbX z)63f64?6EoS}&9VT`l`G=JFpuE*ZKEVT&+Fu~14Qs_63<#tJuyDtO-3l0PYYS5Ee9 z>qvE#I_3&Fe}sKf&8_;>F|h$@R+ETq@YjmSKGjdFArvYE@N4n}oKAdVPYK$Y&pXg@ zi)fc&!|_I16O>FjtBLXJt%g{e=d+@;^$#c)E*|0~B_Gk~t)9efe9znqUh5Ft^PaRA zKE(UoiB}#}2dqiph14$PTqv1!UVe9(-`B{ger^P_S=7a=e9^GWwz5 zroaQ-T{_5HuIX>oKn_jDZLlPou}xFLG}wv@w5}eiK>}+AzG^=Re;*U>W$rFd_)mIe z-nwr<_T!rZyRNZE0bIyylHX~2`{gkCeyU#VP0diCNoq$odIUc^B+IWbmMUA7UZp#- z9;p}eW4pG7?N#3@PXF<%B)K!-IMCglIsG#&aFJVbW#ktAm7V`@}Xt}h* zQrUT&H5h;DsxbvXL0rCaLzLj^2b2s0+nL&J^(^+HlTAN&-*179z4eA= zVZDA}f$3GCo+6an#Q|9YfQl+X)cFv5XWZy$&Dks6)4R0R`XX;1>^e_3sGA4EU0k;i z8kQDXZ5$&YEOeTe#vS*4VvhP8e>6f!5=OAvFT;hY4)abkJy0jbb+Db=F}5!uBwXfa>Rw&JS#rQk^ zSWi^YgDC(I0d3;CubVs~ry%xdq0`{VzPH z)6K0A^*JAprZ$s^72WOKk5=XMPK+=E25t3cI-7n0-nV4dv+fDBxZ}IiRGDdwg>;NVxPUfEcnA74n*N(_ISUH$%UA zrhinsT+cMqeBNDQCl0i9EJyr;FaUsLWX#8?Fc*WYQ1=i`u;3@}j{GdCj1b})v_8u; z){*3v=q;kNNz;O)8|Ej6kDf9BK zNP*M_gcm77x9|3!-G%dg$--n3>Ci16?%=BQ*!o`4Ge=^q&R7&?Jyuwos=fl%jOr#7 zFOZDgw0BtuXDw!3{`AOrRvoP;9z3p^G~`Q@G`=pTeNiz>ep*UOi5n^EcFaUAV)HE@ zxG|eGA~9+Of{X)d8i5t%EsE}LWr8?&XV1^Pn(HC)VHcddE_BkEvW|JwTX9Uy<~ndg z#LxV)sopS_K_Mj$`v*)w`pk4nO!zDGIAOQM39T;i^@dwKvpp5o`tiHUk54$JTZ#0I z8+ns+^g#O2W;_seMo8tJ_u*i`(en8)h4l)sKVK{As;tD(ohe{uwsqW)PAbat@uvBj z_^YLUQ!oFa1fbP-fZ8nhzIEiztDycdRp?%*xpIibq(jLCDWHwE0c){^cU{6SOf2+( zRE^_i+HjV70HXA2u}Q)cJfD*k!~oIjJ}tcHzgfMkV7-9#9U=QoAsX|Bl^GHc35iKR zDs1qsWA*H~)s@>G9un|iVs1Zd1?&WOhwhSpV|UGnF)=gIawqLNbJyF~tygJ*&*r|0 zQ+~6Opd?dsqXfj+$mIN9dK${jP`;m`-zp5IaxDVH;nn>WGr_uW55xNkqXWV+ZTR5Q@CZe)2jdRC#MW? zm|m-A;o$c`+H22og!z8Gwkh4{ltaD{?%tF0Qa~t^6W#}Dg~yl6d8=$}ptT>rW&sC* z+e8jxOu&t+sNV#_3p+ejpg2WqDEW+lK(n~axz{lyY9{n7v%s1(M-hFZfRIsBe0pRoqV*o>4 z<3b`@6pmz*928_Zb1FjVdn4{fjyFV!BPR3sOIhcWGSn(9ZG>Vf4wc{&A!-#9VRbeu@5W8(J&LOWFufDuuyisAXgq{BuQYD9-{JQdwqp*@x3e67L!U1RN`f<_m3 z%ASjkxJh0=kX`2FN<94j*8ar%<&$}MS*J^R_)6%pRVslc!7m0pgDooVy(MjDg%%|` zZXa)mxBB}8D_DJst`HKkf&*2$pq`<;K-(;%IWykx32%S@JIQA?OjI}X8&icAVK*Fl z&h)Ss7nJ|b9|_O*Yl**%zT)j#!i>&K1Zp1Z*tHauVi^^%Zc@|tzkM(wh62Ee+2gwa zq?6F}E1QwADEJ#O%x{eOq91qDqMtM&spP{hw`c1;H?pem@Ye9(2`;w-aJDwwL+5mK zk?h;BasAJ4bIr=xw9AWLvpW}ql<|a2L(`eDM}s~=E!*3dfF_WdW!M_QT&U#RT02M9 zvp4ga*qpXE{aWj65`sT0Jy7(>3_Z*By~B!$OP>SE0S@X@0D zT9C2_R?;e#p5ZU8j@0@8fG0#x8ZE*M=yOAvzS4ANFK}ZV(Bz9CIn8?CzXGNa+GF6S z;0vl=j)nM8LiF>)+u#hL{rJQ{$sEPgapU%8>h8`iE6i{OS)$)AgN`xZ4jP7Ijg|gv z`8~9nY2XZxh&Hd(X^gzx<2*b4>*|elo~Ka-*;_!(ziHrF|1d0pqnlUV2QO&cgVHyu}`J79l`;uAKnw3$j<%CHH?dUoL+q(VvFm9cr z$83lom&BY=FWzOo5W{olUg>I{I>u4so4^EblO?Pzg@pTW$Kj`hPeo@oRGa}k_c{e1-*(m<0CDm$$9lndWa~kmz4cz|&`NseEoK<~CHQOX zMR7HsckMI*xEFQrgaZSK4ZW#8Iu9usdm$N9yxWa$XfY(pQZW6=?hfe)=;JGi?p1D` zw_T3QL)}NakxeqI+m@eWPE)M-Rc?uEWqG2pcaKW#N2d-SaaBKcx+F_^%AkwWy1i8> z106F@RpAZ?lY|fZ*$VEERJ!!b`>_N;m8i(Hy1J$G`VU!-{|5$Sw(g9emk|?b!iIxN zK`jaA{d}Afz5o2UqEuF=_yt_RN=f!?o_fVtxCTd)rt{QIyhJl!DZs{m*$`VY9jl5K z?9mV@-`oh~Dc_Va7EfC^lX%wPG6#a^@28u&M#G&LUKs>;+AYu!vvbo70i=e5{<1X< zn=H<*LsT5?e>_YTsz2Bw_sqwHQ`8u|wgUF%oi4}^Dfg_wH@K7hDr?t?Ty*z}1U!z9 zD31L*ok=ohz&2j5IfOCyx`B>D6uaK$jF1)fIKOhv@x= zJwSnC?47T7z?A@%`}1#E;;gm$sNt#ey2QZ7n}Wl^6a&V2nrnypVh0N|a@HMoEOK*! zysBs8+%X`*ymS|=Mc^Y&@1DTs*C4QJ$~+6a`1`|Gz|nI;f|E{(ik>uhOO;4w_d+{e z_%EzfL{&ibVt*^^8xJ?LxQpGMiwD;&u@;LbUL+PXPPNbu`D(Tk%T7;bXWvX{iACgR z#Om?>!^doQ*}&3>QIiD*W8}P}5?nE4J+VG&sL-6?eYqqD6|KT-QCJZ!q#!SU^p+Bh?jChc?ka_NS zB6i^@m5qQGV&MP(KDn9QKnythq?>8_8|1u3nThR->=?y#w3%hrG zpT!#p|8@I63?Hf&3a;JrDXW;cx2@q}qeHyh-p;h_D^>5%5e(IJOpn7|v z7WULH0(nkx;tJ(Smvs=>NSF%%Rx511vJq0r-9Uc}wj?_I%nim_WBo$9i2I7U7wIy>_0V!XZiwgLV)y$N z-d3E|Bx%IZP!0cYwa+DX7h0KwqySYNq38il4y2wd(uKLx!?y(dw&fsmx(VsvmQS1U zIm8F!*f?Hls9sRqF5(|aP!VxTV$`aDzx)Pxooi|_pWzi$z~pI)>x+Kn_sXT zlN)M*B7I2;@PZ9~rN@9%U&&E#d;>1arRd1A>?E2eWE?LoaYj}r>f|ek-kblFhRV0M z;KeNSzU0+f0%~FyYp8j$2rn3Kxi=fF5%aKg1;rWq2rv~zpg>{rP-kOY5##dJP_Ahn z^)H!2p*TY@Bsu&}!Ppm469Jlu`5H;u)8wVw&$z#k!Ny84j-V zI2@t|%MD$q6cLAlTd>#iO&2ep7BOtIy(SK1Su$*e62MCddz_MO44zMqvY+&CPuA|% z{Mq5yDmuW`xN}88s%_vS5WN59#EbP|`X0NJ-D!%s(O0irDK8-;KM(5BT?T$CgAzbY zrsM40Ze^z_>GZ1v#fN5kF(v(oET|Ps_++e0k>J&l)EUojfZyl3{Y%SW)R{>;-O2OA6Kmm``78 zmf>bZpDNxGUp1Qe3|y(Hg#Ir<{|zrVD2&DL$I5u2*KXoNh5VWd)iIKT>_XM*nAyR% zHVU!U+SqSZ?LDUV@fQ7XKk*QUfjIYX3JrqiIM<_gcE{LVS&K{7 zt;Uju17%F1KGaY8ir3z4iPa2ZzesMluxFedGrUJgFp3i&O9ZZYHlQnP63+cgWljk%LPc3G zh4GG?r4$`sDo}Ii2(4s@+tq5pGI{rza8W>Sn%*%w=VI&i*Q2-)`qtB@6lU@QAEn$O z=vyy%cq8rIp`iTbbLthP9ItrH1jg-RlR^$cb2m5(+IyVx3J1jj!Jz4tgQ((aRYCge z1lcOdYJr{^Q+o1Y5Cv7n`&Sv??=aOxTF3zpWO+@H;dl^lun7Y}{#wVMOrZc0{a<*8 z>*!evJIMT93`U<(awqu^_S5(hl44H(m{I7181%|=X!Ca9#cT{X9Dt?f`#}quM2wN( zUC8`ueEAR6P%WynjRa`;!)3J5M$bYh{L9}PkVXd2&+OAS?2>6c)e?ngm{dci%gx&P zO&C5jVoZ=Jjf^D#=Mb5x8QMfct)8~dlY&Hyu=KL@6H|>R)>Zx)@=e6&9gc*@xl9LO t*f$vvA8V26w{BYUO&N9(wj_wFBV3QZ8Sld6>by{^ea;J@k|8c2Tdfr zbz1|a15<=MR~d5t7Uw{)beb|+EniZJOy4&5CEsHhUyOQhiLGB_wchU{mLPci^?Ye2 zLo)koQdgNjDM!;pXz|iVGR4^A2yK#T(rg^-F3%i(kXxVAj2_o1s_}Z#x-pSy2kCkV z?z0$H;$2VLSvY7LzgB22nPZl>t8Vk%YjY+^&M>Mgbv+s;Glb+ zPeT8)KebjOC5-hd-9%cncDSsZ+Yr20BQ)!?wlUh*Ql3GQ({j+97qi^pwrD+`l7sO! z({J-{{=k^Xd3c?%dyCiIZ)g9d8%gJ!3dGT=aLpHKLr(XiKVtA~p)sklRkx%F4Hhq@ zezjjVCd?RsW`(ijtywi29@RAZJT#FhJ1EXA7!M_0YCn7u>U-4o}`hmCLC z(}I|Mk%=Rc1xHtbcM%nb6QZGlm%5Q;(>kT?*0mLr+? z*S21t=We^sU|PQPsI9KE-QUZKqv0?2`L(6NP;|_SvIlao6niJMZ`0^sfU3k4 zSXC%<*$pU=Q0oSDVGqUj4cCQ|B%!e@FMt2SuKy7;D}T8r zg%7RR+?$tNN3))28}g4D@yQBami}oCYAKlDxvkU#W{7>o6>@gIc!FCQ8(iF;$53K* zyw$uZ+N=?grdAtVDuDkq($WK~o69@smtg9|wr-Wzh9gNn?NDiA5RbejnCF$5)K1u! z0g1Lpx+h{KY&;pcc;+A?zw~zSYL7=C^p6zIdSof@X$sus#4A!P;VHZ88|@x=_iwKK zj00dlML2D6D{m9ttjW6O!cp>g$vcsTfo~EMb|Nhtd!2oqFY+lmK!Y*`7$n7R3z$DR@2U&Wvw;RU9TmrSe&j~YxH zJ}t0mU}VA$14X?tmTc%|0VTT9Cf z<>^^m|72*eVQHkQK09dWd|_47)e9MndIwanX5|-2;3lr4n85IAT46ns#Pz|?B>s9Z zG5rLQb(BX+B!`9}CytMM^sRl}Jk2Q zy=?5)zbSbXzHbh0^aXM-{m) zTmEQ!uC=o-&WDZTFN=qQ*XqOoOq%I zx`#R8N(xqoHqUeBK|sI>71(S(9!m*E%n(g80&>e}OnG;{luE9cka?R_%li}H9Y?Cw zQ`VhquPLx>;YYWNU2>8K1odWk`mgb1KDX#(y}(&tt!e#cs^c%X>Cdum?O=3yi>RkltGZ3?ND{V`VC&eWJw-r|6eH92M zY3-U%{R!sfM%6Kw&ugOI<|goa?Ydd+i>n(AQ2VIklR2K$X^$HCJ5dZ3N|(sEChisF z1di_gFfgFBr{8CR#$%#ue#A-AwxMX-s9^TO4iXNIJYkng=u~+M_Xp`mLC(A7wM}*wAK(D0y#XZte1;{Ih#?+}_yW$Bk#5R*>AQ#-NH|DPLxtQwj z?bZ_-cvY~_C)oTbAo!&H4Y^!^YpW^(24EhvtHuXVBI>uY#=?s8RDqZR_%tr{m+^jx zg@6w5QynOkzd0)C)*cJ(==jmxx?Kfr)|i5$r&8ltv*zGv*4~fE>Y_C^R;1tK%0x7e zo*MZjzDe~=hrH$j!tpHSRZ3@a`uowZh!groBYaBEg*W-?`wn~ZKZ0KPvlG(TJf@`O zwDM~w500Sd_sCnh*1pHXC5z^MLut)JK5O;ytHaA5r$DwB1p7!8Dw~ryo5>a)R`j<< zpouiTG`YOavjiUl>>OX>Iqdk+B|0yz;x`Qt5<%)o^*02cYlyqOe2z*VMG@n41xH}i zapBs_uwllEnc)D#BsfS-W#W0&uzXHsJhOLr727bB6|Co-J?qrM9a%6QVn zA;b6%7>+Xp5CNAn>QiM$9++lgY`c)z(30>79a;T_5Qs+_yJYvqjQ^wa!ekrv3_UZEe%wLOz zS{>4J8$LiXr)jMO9Kdf37v$#v3`)2DylveEUpW%9Qo$>u=X3Wu6kKQ{+<;Tkm?{aF;T>x3(qx3Nj$j;11@u1Gbk zIBvm)$P3)!F8QLA%)}IO+SeaH8aC%HyK%}~0#tQgr7$Cpqgo`@itGVPfG&G&JQh|y_Uv{3|R%%A>m6b#QUiLDdcCnn_PW{A(3TNUVT zaBpRnc8fvlMwwc$a-KfY$yll$Yck@2?F-Xy4d1>ym}Y&*yOv>j3E=1IlKB)oa2_(G zkw2UD8r`+77y#5$@c~YJtL4#yug3Qp!X0MkE{hmwXvV^a5`hPH> z55%d{;T8Y!;u)#Q{S><^M%nrsc@~P`*2QORb7WHDmq$juc9e)%J8IkB$jQ_GuUe!Z zi5}e-qoPR6?A|)~wf>UBo+M=@MeQ6IU@Qr`%ffw)>nn_cENX2(DdJ{SWByO zf9jl3RLYtTS=?x0AEvu7{E>+XD;Qqjt61!HGd%A=Cnz#foU}&Yunz`M6Wl)##hR*_bRF5{od5j6jvrJTnXIo$preIULzt zUvy-(NWO<#ZImkOb|VBEaF#nJwbtV;{;5<^y}NkG&|BKNu^o{AlbflJnht3W`lWF*HB#-VHM~-5*U7ZhA%4+e6K#h65JU)#b2}=M`{}VAp zBeFSyj*O(J)nlXdm3@wJSHQ)}A7JHj8sYw1_mUh|@db&@PX%H|VQCIgUI8D| zCPQK?(^snR1u*Ju7#F^LLC zFj~Z>gvW^Vk>@84sxCQ4&Y$}hUVGFp996VU=&+#S5sY!VIMnv0^d?xcCm6l+U8<9c zYWL>a4rCi$p3*DJK}&5I@021P^`Mx!d-Z9t0|hQ_y8D5OxJ;Pu_*n&isT5EPp3s|L z;htRZ4AKEQ;HqBin;a2I)@Zs8A5EpN8Sjr~In-Z+RP!EB!2gIa zGRutfOjL-EUH#a2p$Su4{jqE7^lOw}|Q&uPiUlFLu})!QlX35w8;QSO+~+ zPYm1Ym9hc=`S8tcD^5X4@H|uuy#W9MiiZacke>M%CB*SjeXWSIOGt@MB22pH{T$U^ zD^*pH)d^TQTsC*mnWyhOU9C$JQsS4ouHJi3*`Qj2hg*o~vRd7bTZno;K~3F#)EaGp z#aQsjk9y%xPdH~ZZ!J?JLE-!-t66FSutTguWVi*@i{(Pc1;co$5{I(5iUyJt!L;)X z(W|A5o6wy*v`O!sv8>CsC0m}g7vS0-~3kTM3^gid!9o1Amc!r^7dqVK@M{;KQseJdP%f6RGzHy8W` zIiU4Dwi?wcyqsRAQ{_=SiA>68mS1y@`yliiE8~Ogl6nM!0RRAEpiaX`5aZ-UMo#`J z@J6L!V(j4O;;k<`~_mV+*vm`2Ei3w$R~O-0SV@ zEx5Jc-RSS6WA_Fj@_*`R0Q7rLs_JeIs&-(b0siLx!$dXdac0C`5#)Egc-Y zHZV-{RQC3-3(B`l|Ev_d&y*2&t~B-4c$>Dv{M$%m;Gajr%0-XGsSn`7l?{uD+4G0% z1N!;Mh~NziQzSfU)r2VDtfThSq6Q{jMtp(@yt_5Pka=i~{`^V|-4CG0Yhm(*Hy?w4 zZ^um(-<^@Q-zt=&aHh3(V<>pObqU1{!> zeo)$=t0v{--PJ_qsm(gc1-eb*K&J+N%`u|1tqedy95s~~BEnaq?LM5;P%%_Usdq(i87VJxPQ;v+%37QWXZ23E9U|-Vkl}QLAMXu<(@o{6M zx#V2P2*`9i=5Bk$mmYG_QM0paK8Eaesun%>)2-gwD*bhN8icf|%K_U|lb!rVT$v(g ztH&1UJDaWVkxKtkDTNqn(!u#{HmRdp@+OyeHYOE53cjssAso3}=09FVw(Xo(mt4V) z|2I15X&~M3BSYN{Y4PponLBbYq^j3GwF#bA*aUC$jth$(T#rP2H|ebwdPDx8vRQo%ZhCoScAwF};ebgA|#Z$2)+tO#-HYjMm6 zw`-|lcRotGb3(8c?wnklGBEDq=gWCeOlI8Ot|E-hZ%9BNk%woR=L1b=mi{3HZ9bvM zl#x_1H-z_vQB?8>O2a>ozp@AT^d85~yk-Tu*-F#jv@G;T?}$X=!ev{>g)RU-iakdz z2pV+Z%S1&xTEL6Ex#AkM4s@T$H~ai*vhL>9KBkFC>cK5}Na#PtU3iAE z7C)okh`lykynKlZJsm-l6gEFqSvVDK?(cx5p-BFJ&!RQxxPgi3tjZdPf{s5BN*h zWLLi`={P<)P}#T>qLyQ@cp-?#p*yHz(!#^Ult{8vFF?k8=bc3J-f^$yQdpA{^S``5 zN(gbTYyG9BKiZN#WnYiVSxyQIj{BQ=vetyZDN=#}nGLLr@rA1)X$ecXc*nH=?1DM; zmbbJJ`L}u$+?9QPZn}E16f^jr6oMol9+1>#!7ZHx0qujUtw$iL&a=z2@@+6#124H| z^RmCh(H#z;Hz|-mWB&u*gC`fXq7wctJELoeGwrqRwO0+kP+d}4d6aiopJD$fb>k-6 zu4M7nF&dDb!i8*1A=To1cL2>Cyg2LPs`git&G%mHX)7!o3~ahuPjj)jA^c3@gC3ll7#yX4BCW05M}fr zpiys>t@5M&iZsnaxi$&TWts6R?DN#lVe>DGLMYyUSZX2}eRUtv=nbn<2_Bz$M&=2n zF%a^SBqa+B1~w-^+=(EtR^QFzWB9%7L&m=_7!c2K;dqjkaTyP{ z)>Eiv4%TPwbP*m9yuf}LySvN$`syLNQwcy%&SF{q9^6@mmoemdyb82&RlpkbTZq|8`BNio7Xj`xtu;si8 z*pYa(;dVv8Yg|7tk@o(-A$u_hNWD8YO%fv$16Vw(LAko_sIxg#l41-FU#+>CG2cxo zD~Lc9I$8w3Tj8L^b}o8p0H87%ZOs^E%>Vu58Wr6&84&+B<@kQ*K6EIPB6zrbyS&ANU42PV{ za}r}_+qwUGKcbN)dZwaq=8doeYQ^p5JV1M$N>rju1$p3ABAwtnb4h;GsK7`>nxQ>= zj`}_7nmWxf*F302EOAYSw(&7y0aPU4QkW|ghzF@=+w@lui_G~-NE?bfNFW($e1KYxAKD(on-kvgBUBwV+uR;nJYx}L9sNi{ zN|r{b4?UQBfK>W5kpIaD*WbZjoe&_O2vC6kH**Ar6%n@jA|a;2mts=d$vsA5vj?EL zwDjdPxTsoms8=U(go_K=9K?%kroojM-Nr%hs@l*lmxZK-JcsT<1u!~kQI1_4^t+7e zyPAvRTJ@Husru5>RW8?}=Y9O%S;q5pFu%Jg6|HlJw$xqvJ6tRjOQry4wGghadk8c3h7i2iKHt=ZvCI zLX?OAs0!aSqO2XqM{gMTsw971} z8t(qUM(aD2clLWXKZfvVuI3!^yM(&RSdPOYu2v#6S>sHCT-r>v%(rlXyrpq#p) zLF;6smXnW>jfIblhK`AXjE9AchK8PsKZb^ehJ=2DgM-yhj%<2zcyejLHg~c)a~(mFi%E1B~?ixNkCF4CL<#wB_bUg4+8)I0Q8KfrT_o{ z0d!JMQvg8b*k%9#1G-5>K~#9!wbqGt+Bg)(@jI#uw;eRhSF?4~AW!1N*^~EwxPH3E zAPeZ;-*MR#{VV%m9i-|>Q&&_s+Z{Plb!{b5jx+G`*WV0OJKOE$&E@5BfJ1F(<3ghV z<$;?=x;Yi-+B7caX#9Hu?>_uu;KRF9fq&#t~oyZE4)UPv{WOsa`Ue+~F-_TLMstHin4%u#x*lDe);EfY%P)xQ;wVLXM}ZXcY@W-_Dx zsme_fet3WX`qk@K_xIVjeJ?EFIB~#&I}y0QyEb?C!?>+r2)I1pjsni*0p~_5*lf02 zJ9#m7DYo0qURCYJudjjYYwgGTfUUs$tE;UeD{z?OS64oBd&_YGxx+XoGvMXp$14`d z3ArutgO8b87RYUZ?P$EJ_#g=YeM_=~C!r@d-;o_vg`5J!_-la}!mkBF`1ute(GU{_ z6y}Ls?v~FS{k{N|fwisy zK74lo#DdKSt@{<64}?M)sH*-^&S}D4lZ*m9Di+d%d`m;}Er0})pQWnj18?b%2jnY( zTBw!5G99K7y|~cyq!vg5AbU`fUbmmGOCEio(Ti63xYK_oI2V9$(^hAcFZsuz?ZOZM zED%B#fPHK_AO)c2KLrEQgsu&ZpeIQPwd}zh-gwyn&MlYD0UPn}rD?lH-b*IbSwIu# zH0iwe!hg%9A0E@pu9Z=MvVbSYgr`o(3O)pH^9gMS5hpq4#&=@~(TK$1BofH%RLH+%@OA)jbkf%={TIs`&ZL*V>=paAll%V}}WC~m4!jNb}C zj{^CG5aO=|VhBHW3U#^72c3hIhfA~vtlbch)C3OKjUXbe%})XQ-OvYgXHETlbu*nQ zq!|YwZD#@1`6>Z04gh_?3W^Y#d{>~hQ?nY30`vLY%TOR3ZWz$%K#t4upNuoUuES_Q z@V@PQ0hyJ^%I2s0fmXGwM*D$?wB_#wIfrwcJFy>>zZayiNCZg=pxh5irvp&c-E}|O z557A<7AW_Fk`&S(*TZeimFN3GV~XDk$AMrQo?+<=KC8OJ9@m3G00000NkvXXu0mjf DDoZ!s literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/azurelib/textures/item/doomicorn_boots.png b/common/src/main/resources/assets/azurelib/textures/item/doomicorn_boots.png new file mode 100644 index 0000000000000000000000000000000000000000..ccb401b3b7c07bad70354a4b77649c2b840ccdc8 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9JOMr-u0ZL4Lsu|2a5La|ZYV`Rbl7jv*Ddk{y_PBxmvQ^cb!(P;wEOnZPT^n;-xr8in|J c6f=G?s@`Q!ymNKoe4rKvPgg&ebxsLQ0Q|5bX8-^I literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/azurelib/textures/item/doomicorn_chestplate.png b/common/src/main/resources/assets/azurelib/textures/item/doomicorn_chestplate.png new file mode 100644 index 0000000000000000000000000000000000000000..45562d9718955c8fbd368a18f64dbc719074afb6 GIT binary patch literal 338 zcmV-Y0j>UtP)F1LIeVqV_tpJ<<|Nrgn>!kpXp#Y5h{QRB(g7ozA%FMu$1bOlD@Rb01q5z56*wFd; z__M8g3g+uYRm_x6wmaOCCS_xSeo^z)PedYS=! z^YikGiG`sMeaXqhq@m?B kYkHijVXVdqcf)|ECc-EPMph;4;Q#;t07*qoM6N<$f;p_C3IG5A literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/azurelib/textures/item/doomicorn_helmet.png b/common/src/main/resources/assets/azurelib/textures/item/doomicorn_helmet.png new file mode 100644 index 0000000000000000000000000000000000000000..c969da1462109167051b4cf6bbb7a95d7f03d85a GIT binary patch literal 503 zcmVAcnK_+8dYQH}NJC5uGH6INpbI~Pm!)DzFfne8#`uvm!Ika|>Pk26L|j;d zX@ay?TCvcfH=&)GV{D8G(7Lek?4RFx^Iil)J~3pT{{uXE`bd?Edwkt0@MFIP2&B}R zn?KF%TlWrqdaQzHD-YFfd5iq}RXigEQliNaBhdu56~{{1+_--CPwo(~^5U_ob()k5 z>+F1dOW2Gf!(mVYO*iPqVvL>0GHGYIa%riTJpwGNcRL$wyA>upL8nn8@IAbCi^jJK zZD*P(XO8*AUw~Ji*Vu5&Oe8Io@1cT#An@@I4(N`K;8+>Xr=|v1@TQdGLv@po9wHKn zAf@b0S_pv`_&CvVE?B1q1MBV=a@(6!>n>)(#4rq$QV0YF8q@JKv&qb0pt@V8P_Ix_ z`zWEI34ubP0VfHQjg4a^(_FoLvwsuwtFQU)mWj@sBk)a>FA=(jCiiINb0i`uGG{Nc zaP3Zip!8{tx?5w^c4#)B+X1ZyH0inpVrHDN$yt^b9~=jMHS77yWz}kWl#2D=o7nD6 tTh|3MXVTbqjKzif{TJz94r}OR`U#`sxe@0ys=)vN002ovPDHLkV1o16=w|=` literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/azurelib/textures/item/doomicorn_leggings.png b/common/src/main/resources/assets/azurelib/textures/item/doomicorn_leggings.png new file mode 100644 index 0000000000000000000000000000000000000000..5d252cff21c34684e7b62c949585c39b804dd715 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`L7py-Ar-f-2D&mHFc9#zRucXg z@INjs$>)o-F>BrN_UnoU4gxHqVh<|g^xgCP=k-mrXsj0zn0tJ}ay5m_d0G#4)m46D mdzrq!K(1-YS`i+bRw>C?{(lv_eYOHkXYh3Ob6Mw<&;$U>X)TQa literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/azurelib/textures/item/pistol.png b/common/src/main/resources/assets/azurelib/textures/item/pistol.png new file mode 100644 index 0000000000000000000000000000000000000000..290e21ba10d8170a3d0bb0d087335cfad19524f9 GIT binary patch literal 1486 zcmV;<1u^=GP)z>$|NsB0RVx4g|G$7>s#Q7Y=;+b8xzTi8xkfIwTQ|da zTC__%@$vD$SwQpi^WMC%yjeHQdQ$7_>%CPu-`Cg2cv#7DQN?so#&}b{Up&KWLBL)* zzhpeZWj)1gLBeZ2$$e78Z9&CvKfj!a%!XRTZ$Qa-Bj$Xy7lF*J>%!5eKk6O%yNzaH&)}3nAn`Y3FPR)l<)S6Y(mQd84 zSJt6f)t^-0xqaHKU)ZQu+_7WcvUJ|GX6D+!<;9HPx^Cynkl?&(*PvkO(3k7fp5ni6 zC=Ay|NsC0{{R2{|Nr{`|M>s@_5S_){{8v> z{rCR<@c#Ym{rvO&`|AArCH#tB@Q&?JFWoKz@adUr#hL4$^qo$~-ud=ndy2HlK(AC-6-s0u#?C$UI@$>Wa z_V)Jo`1twy{Qdp@{{H?9UglZ=00W{)L_t(|+U?bam)kfHfN^b^nVIRB%XrLKW@fG& zYi9oU`{c-Z-aPxTN@;&j8$&_gPRN<6B?^IpO9??t;eBZp*JhHNv=Uu^=900oF*gIV zFgKZjDu5rBN>NBair_)weUnnU116y>U=jua_^W`n38-)Y$9S`sCqO0rn!^Q97OI(; z|u?llB6@!452|x|R8A9HG zw*66-C674+go($1wr2po5fHKk#Gj4{c<-$eLSe4MJNOKTaR9Ti86RRd-ot#%!(P0F zWtf3~62N8tfc7^5>K7qizyttAzJS5IK#f(FJ}&sj3K$?(6t2U1?86VZfMfU`zu-rF zi4UwD)KOJn90jffQhtH9)IX0w4e&0LV0f!PjvLNUs4XVECJW zIdJ#{cX1CFa2^kE61#B*8?YpqgW&*QDIXJ&_8x732N2%}W+h-1$r8Xe6$!#B6JMqP z-w4nV1aQLiQ-C*P6oS8;0I(Xna0@qZ6Ay764{-^{unluD3oi1$EdW&j2diI*g^nh` z$AJ1?Nbke^0qI3JE`Vz>AmdXIf{3;SVCY6?z)bAIA)LSsT*Wn9!FAliY3#-%OhG?@ zjjaw~G4`|{)F7pB14Whq#@q-n9%6v1_lDS`LRPPrE5Q0w_8hClZjFH0V^PKcgh2q* zngR3}^e+Q8;~>7mNj$=RTuQFsJ}zQAT+$Mt%Apa^zu=HJKzzk)5YXQ_zC%V3fD{0= zL4YrVsB*zHBwqj<*IzpZqyW#hD$M}bWeWD=Yn;GC+{a~H!Y$mvQ+$E<(H-zOosh27 z3vkcNM(n{Y+(>TY3ZCF59^eSxhD#;_UXOr9_!K|mChp=WPT(ZI#W~!-co oYtg*tumWz#B-F==fEOO{CwClw<{7>lW&i*H07*qoM6N<$f-?r#K>z>% literal 0 HcmV?d00001 diff --git a/fabric/src/main/java/mod/azure/azurelib/ClientListener.java b/fabric/src/main/java/mod/azure/azurelib/ClientListener.java index d55644d2f..e3413e872 100644 --- a/fabric/src/main/java/mod/azure/azurelib/ClientListener.java +++ b/fabric/src/main/java/mod/azure/azurelib/ClientListener.java @@ -3,11 +3,22 @@ import com.mojang.blaze3d.platform.InputConstants; import mod.azure.azurelib.network.Networking; import mod.azure.azurelib.platform.Services; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; +import mod.azure.azurelib.rewrite.render.item.AzItemRendererRegistry; +import mod.azure.azurelib.testing.armor.DoomicornArmorRenderer; +import mod.azure.azurelib.testing.block.be.StargateBlockRenderer; +import mod.azure.azurelib.testing.entity.MarauderRenderer; +import mod.azure.azurelib.testing.item.PistolRenderer; import mod.azure.azurelib.util.IncompatibleModsCheck; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; import net.minecraft.client.KeyMapping; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; import org.lwjgl.glfw.GLFW; public final class ClientListener implements ClientModInitializer { @@ -26,5 +37,20 @@ public void onInitializeClient() { KeyBindingHelper.registerKeyBinding(Keybindings.FIRE_WEAPON); Services.NETWORK.registerClientReceiverPackets(); Networking.PacketRegistry.registerClient(); + + AzItemRendererRegistry.register(FabricAzureLibMod.PISTOL_ITEM, PistolRenderer::new); + AzArmorRendererRegistry.register( + DoomicornArmorRenderer::new, + FabricAzureLibMod.DOOMICORN_HELMET, + FabricAzureLibMod.DOOMICORN_CHESTPLATE, + FabricAzureLibMod.DOOMICORN_LEGGINGS, + FabricAzureLibMod.DOOMICORN_BOOTS + ); + BlockRenderLayerMap.INSTANCE.putBlock(FabricAzureLibMod.STARGATE_BLOCK, RenderType.translucent()); + BlockEntityRenderers.register( + FabricAzureLibMod.STARGATE_BLOCK_ENTITY, + (BlockEntityRendererProvider.Context rendererDispatcherIn) -> new StargateBlockRenderer() + ); + EntityRendererRegistry.register(FabricAzureLibMod.MARAUDER, MarauderRenderer::new); } } diff --git a/fabric/src/main/java/mod/azure/azurelib/FabricAzureLibMod.java b/fabric/src/main/java/mod/azure/azurelib/FabricAzureLibMod.java index 995f7229f..d5b398882 100644 --- a/fabric/src/main/java/mod/azure/azurelib/FabricAzureLibMod.java +++ b/fabric/src/main/java/mod/azure/azurelib/FabricAzureLibMod.java @@ -7,19 +7,40 @@ import mod.azure.azurelib.entities.TickingLightBlock; import mod.azure.azurelib.entities.TickingLightEntity; import mod.azure.azurelib.platform.FabricAzureLibNetwork; +import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; +import mod.azure.azurelib.testing.armor.DoomicornArmor; +import mod.azure.azurelib.testing.block.StargateBlock; +import mod.azure.azurelib.testing.block.be.StargateBlockEntity; +import mod.azure.azurelib.testing.entity.MarauderEntity; +import mod.azure.azurelib.testing.item.PistolItem; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; +import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry; +import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.monster.Monster; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.level.block.entity.BlockEntityType; public final class FabricAzureLibMod implements ModInitializer { public static BlockEntityType TICKING_LIGHT_ENTITY; + public static BlockEntityType STARGATE_BLOCK_ENTITY; public static final TickingLightBlock TICKING_LIGHT_BLOCK = new TickingLightBlock(); + public static final StargateBlock STARGATE_BLOCK = new StargateBlock(); public static final Enchantment INCENDIARYENCHANTMENT = new IncendiaryEnchantment(Enchantment.Rarity.RARE, EquipmentSlot.MAINHAND); + public static final EntityType MARAUDER = mob("marauder", MarauderEntity::new, 1.5F, 2.5F); + + public static final Item PISTOL_ITEM = new PistolItem(); + public static final Item DOOMICORN_HELMET = new DoomicornArmor(ArmorItem.Type.HELMET); + public static final Item DOOMICORN_CHESTPLATE = new DoomicornArmor(ArmorItem.Type.CHESTPLATE); + public static final Item DOOMICORN_LEGGINGS = new DoomicornArmor(ArmorItem.Type.LEGGINGS); + public static final Item DOOMICORN_BOOTS = new DoomicornArmor(ArmorItem.Type.BOOTS); @Override public void onInitialize() { @@ -27,10 +48,41 @@ public void onInitialize() { AzureLibMod.config = AzureLibMod.registerConfig(AzureLibConfig.class, ConfigFormats.json()).getConfigInstance(); AzureLib.initialize(); new FabricAzureLibNetwork(); + Registry.register(BuiltInRegistries.BLOCK, AzureLib.modResource("lightblock"), FabricAzureLibMod.TICKING_LIGHT_BLOCK); FabricAzureLibMod.TICKING_LIGHT_ENTITY = Registry.register(BuiltInRegistries.BLOCK_ENTITY_TYPE, AzureLib.MOD_ID + ":lightblock", FabricBlockEntityTypeBuilder.create(TickingLightEntity::new, FabricAzureLibMod.TICKING_LIGHT_BLOCK).build(null)); + Registry.register(BuiltInRegistries.BLOCK, AzureLib.modResource("stargate"), FabricAzureLibMod.STARGATE_BLOCK); + FabricAzureLibMod.STARGATE_BLOCK_ENTITY = Registry.register(BuiltInRegistries.BLOCK_ENTITY_TYPE, AzureLib.MOD_ID + ":stargate", FabricBlockEntityTypeBuilder.create(StargateBlockEntity::new, FabricAzureLibMod.STARGATE_BLOCK).build(null)); + FabricDefaultAttributeRegistry.register( + MARAUDER, + Monster.createMonsterAttributes() + ); + ServerLifecycleEvents.SERVER_STOPPING.register((server) -> ConfigIO.FILE_WATCH_MANAGER.stopService()); if (AzureLibMod.config.useIncendiaryEnchantment) Registry.register(BuiltInRegistries.ENCHANTMENT, AzureLib.modResource("incendiaryenchantment"), INCENDIARYENCHANTMENT); + + Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("pistol"), PISTOL_ITEM); + Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("doomicorn_helmet"), DOOMICORN_HELMET); + Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("doomicorn_chestplate"), DOOMICORN_CHESTPLATE); + Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("doomicorn_leggings"), DOOMICORN_LEGGINGS); + Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("doomicorn_boots"), DOOMICORN_BOOTS); + + AzIdentityRegistry.register( + PISTOL_ITEM, + DOOMICORN_HELMET, + DOOMICORN_CHESTPLATE, + DOOMICORN_LEGGINGS, + DOOMICORN_BOOTS + ); + } + + private static EntityType mob(String id, EntityType.EntityFactory factory, float height, float width) { + final var type = FabricEntityTypeBuilder.create(MobCategory.MONSTER, factory).dimensions( + EntityDimensions.scalable(height, width)).fireImmune().trackedUpdateRate(1).trackRangeBlocks( + 90).build(); + Registry.register(BuiltInRegistries.ENTITY_TYPE, AzureLib.modResource(id), type); + + return type; } } diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java new file mode 100644 index 000000000..8e8d75432 --- /dev/null +++ b/fabric/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java @@ -0,0 +1,53 @@ +package mod.azure.azurelib.testing.block; + +import mod.azure.azurelib.FabricAzureLibMod; +import mod.azure.azurelib.testing.block.be.StargateBlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class StargateBlock extends BaseEntityBlock { + + public StargateBlock() { + super(BlockBehaviour.Properties.of().sound(SoundType.DRIPSTONE_BLOCK).strength(5.0f, 8.0f).noOcclusion()); + } + + /** + * Creates a new {@link BlockEntity} instance for the Stargate block at the specified position and state. + * + * @param pos The position of the block in the world. + * @param state The current block state for this block entity. + * @return A new {@link BlockEntity} instance associated with the Stargate block, or {@code null} if none is + * available. + */ + @Override + public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos pos, @NotNull BlockState state) { + return FabricAzureLibMod.STARGATE_BLOCK_ENTITY.create(pos, state); + } + + /** + * Determines the appropriate ticker for a block entity to handle its periodic updates. + * + * @param level The current level or world instance. + * @param state The block state of the associated block. + * @param type The type of the block entity to obtain the ticker for. + * @param A subtype of BlockEntity. + * @return A BlockEntityTicker for the specified block entity type, or null if no ticker is applicable. + */ + @Override + public BlockEntityTicker getTicker( + @NotNull Level level, + @NotNull BlockState state, + @NotNull BlockEntityType type + ) { + return createTickerHelper(type, FabricAzureLibMod.STARGATE_BLOCK_ENTITY, StargateBlockEntity::tick); + } +} diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java new file mode 100644 index 000000000..2ef220aac --- /dev/null +++ b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java @@ -0,0 +1,26 @@ +package mod.azure.azurelib.testing.block.be; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; +import mod.azure.azurelib.testing.CommonStrings; + +/** + * The StargateBlockAnimationDispatcher class is responsible for managing and triggering animation commands for block + * entities, specifically for the Stargate block entity. + */ +public class StargateBlockAnimationDispatcher { + + private static final AzCommand SPINNING_COMMAND = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.SPIN_ANIMATION_NAME + ); + + private final StargateBlockEntity stargateBlockEntity; + + public StargateBlockAnimationDispatcher(StargateBlockEntity stargateBlockEntity) { + this.stargateBlockEntity = stargateBlockEntity; + } + + public void serverSpin() { + SPINNING_COMMAND.sendForBlockEntity(stargateBlockEntity); + } +} diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java new file mode 100644 index 000000000..bf053d520 --- /dev/null +++ b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java @@ -0,0 +1,32 @@ +package mod.azure.azurelib.testing.block.be; + +import mod.azure.azurelib.FabricAzureLibMod; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class StargateBlockEntity extends BlockEntity { + + public final StargateBlockAnimationDispatcher animationDispatcher; + + public StargateBlockEntity(BlockPos pos, BlockState blockState) { + super(FabricAzureLibMod.STARGATE_BLOCK_ENTITY, pos, blockState); + this.animationDispatcher = new StargateBlockAnimationDispatcher(this); + } + + /** + * Handles the tick behavior for the StargateBlockEntity, triggering server-side spinning animations if the block + * entity and level instance are valid and the method is executed on the client side. + * + * @param level The current level or world instance. + * @param pos The position of the block in the world. + * @param state The current block state of the associated block. + * @param blockEntity The StargateBlockEntity instance to operate on. + */ + public static void tick(Level level, BlockPos pos, BlockState state, StargateBlockEntity blockEntity) { + if (blockEntity.level != null && level.isClientSide()) { + blockEntity.animationDispatcher.serverSpin(); + } + } +} diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java new file mode 100644 index 000000000..dd9e259cd --- /dev/null +++ b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java @@ -0,0 +1,38 @@ +package mod.azure.azurelib.testing.block.be; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; +import mod.azure.azurelib.rewrite.animation.impl.AzBlockAnimator; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +/** + * StargateBlockEntityAnimator is responsible for managing and configuring animations for the StargateBlockEntity. It + * defines specific animations and registers them with the animation controller system, enabling dynamic and interactive + * visual effects based on the block entity's state. + */ +public class StargateBlockEntityAnimator extends AzBlockAnimator { + + private static final ResourceLocation ANIMATIONS = AzureLib.modResource( + "animations/block/stargate.animation.json" + ); + + protected StargateBlockEntityAnimator() { + super(AzAnimatorConfig.defaultConfig()); + } + + @Override + public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { + animationControllerContainer.add( + AzAnimationController.builder(this, "base_controller") + .build() + ); + } + + @Override + public @NotNull ResourceLocation getAnimationLocation(StargateBlockEntity animatable) { + return ANIMATIONS; + } +} diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java new file mode 100644 index 000000000..49be22d61 --- /dev/null +++ b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java @@ -0,0 +1,21 @@ +package mod.azure.azurelib.testing.block.be; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRenderer; +import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRendererConfig; +import net.minecraft.resources.ResourceLocation; + +public class StargateBlockRenderer extends AzBlockEntityRenderer { + + private static final ResourceLocation MODEL = AzureLib.modResource("geo/block/stargate.geo.json"); + + private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/block/stargate.png"); + + public StargateBlockRenderer() { + super( + AzBlockEntityRendererConfig.builder(MODEL, TEXTURE) + .setAnimatorProvider(StargateBlockEntityAnimator::new) + .build() + ); + } +} diff --git a/neoforge/src/main/java/mod/azure/azurelib/ClientModListener.java b/neoforge/src/main/java/mod/azure/azurelib/ClientModListener.java index 9775fd29c..3be5705bb 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/ClientModListener.java +++ b/neoforge/src/main/java/mod/azure/azurelib/ClientModListener.java @@ -3,9 +3,17 @@ import com.mojang.blaze3d.platform.InputConstants; import mod.azure.azurelib.client.AzureLibClient; import mod.azure.azurelib.config.ConfigHolder; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; +import mod.azure.azurelib.rewrite.render.item.AzItemRendererRegistry; +import mod.azure.azurelib.testing.armor.DoomicornArmorRenderer; +import mod.azure.azurelib.testing.block.be.StargateBlockRenderer; +import mod.azure.azurelib.testing.entity.MarauderRenderer; +import mod.azure.azurelib.testing.item.PistolRenderer; import net.minecraft.client.KeyMapping; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.ConfigScreenHandler; +import net.minecraftforge.client.event.EntityRenderersEvent; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModContainer; @@ -21,6 +29,27 @@ @EventBusSubscriber(modid = AzureLib.MOD_ID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) public class ClientModListener { + @SubscribeEvent + public static void onClientSetup(final FMLClientSetupEvent event) { + AzItemRendererRegistry.register(NeoForgeAzureLibMod.AzureItems.PISTOL_ITEM.get(), PistolRenderer::new); + AzArmorRendererRegistry.register( + DoomicornArmorRenderer::new, + NeoForgeAzureLibMod.AzureItems.DOOMICORN_HELMET.get(), + NeoForgeAzureLibMod.AzureItems.DOOMICORN_CHESTPLATE.get(), + NeoForgeAzureLibMod.AzureItems.DOOMICORN_LEGGINGS.get(), + NeoForgeAzureLibMod.AzureItems.DOOMICORN_BOOTS.get() + ); + } + + @SubscribeEvent + public static void registerRenderers(final EntityRenderersEvent.RegisterRenderers event) { + event.registerEntityRenderer(NeoForgeAzureLibMod.AzureEntities.MARAUDER.get(), MarauderRenderer::new); + event.registerBlockEntityRenderer( + NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), + (BlockEntityRendererProvider.Context rendererDispatcherIn) -> new StargateBlockRenderer() + ); + } + @SubscribeEvent public static void registerKeys(final RegisterKeyMappingsEvent event) { Keybindings.RELOAD = new KeyMapping("key.azurelib.reload", InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_R, diff --git a/neoforge/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java b/neoforge/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java index e6734c3b3..146a0984a 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java +++ b/neoforge/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java @@ -7,10 +7,24 @@ import mod.azure.azurelib.entities.TickingLightBlock; import mod.azure.azurelib.entities.TickingLightEntity; import mod.azure.azurelib.network.Networking; +import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; +import mod.azure.azurelib.testing.armor.DoomicornArmor; +import mod.azure.azurelib.testing.block.StargateBlock; +import mod.azure.azurelib.testing.block.be.StargateBlockEntity; +import mod.azure.azurelib.testing.entity.MarauderEntity; +import mod.azure.azurelib.testing.item.PistolItem; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.entity.monster.Monster; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraftforge.event.entity.EntityAttributeCreationEvent; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; @@ -35,6 +49,9 @@ public NeoForgeAzureLibMod() { AzureEnchantments.ENCHANTMENTS.register(modEventBus); AzureBlocks.BLOCKS.register(modEventBus); AzureEntities.TILE_TYPES.register(modEventBus); + AzureItems.ITEMS.register(modEventBus); + modEventBus.addListener(this::createEntityAttributes); + modEventBus.addListener(this::commonSetup); } private void init(FMLCommonSetupEvent event) { @@ -50,13 +67,46 @@ public class AzureEnchantments { public class AzureBlocks { public static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, AzureLib.MOD_ID); - public static final RegistryObject TICKING_LIGHT_BLOCK = BLOCKS.register("lightblock", () -> new TickingLightBlock()); + public static final RegistryObject TICKING_LIGHT_BLOCK = BLOCKS.register("lightblock", TickingLightBlock::new); + public static final RegistryObject STARGATE_BLOCK = BLOCKS.register("stargate", StargateBlock::new); } public class AzureEntities { public static final DeferredRegister> TILE_TYPES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, AzureLib.MOD_ID); + public static final DeferredRegister> ENTITY_TYPES = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, AzureLib.MOD_ID); public static final RegistryObject> TICKING_LIGHT_ENTITY = TILE_TYPES.register("lightblock", () -> BlockEntityType.Builder.of(TickingLightEntity::new, AzureBlocks.TICKING_LIGHT_BLOCK.get()).build(null)); + public static final RegistryObject> STARGATE_BLOCK_ENTITY = TILE_TYPES.register("stargate", () -> BlockEntityType.Builder.of(StargateBlockEntity::new, AzureBlocks.STARGATE_BLOCK.get()).build(null)); + public static final RegistryObject> MARAUDER = ENTITY_TYPES.register("maruader", () -> EntityType.Builder.of(MarauderEntity::new, MobCategory.MONSTER).sized(0.6F, 1.95F).clientTrackingRange(8).build(AzureLib.MOD_ID + ":marauder")); + } + + public class AzureItems { + public static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, AzureLib.MOD_ID); + + public static final RegistryObject PISTOL_ITEM = ITEMS.register("pistol", () -> new PistolItem()); + + public static final RegistryObject DOOMICORN_HELMET = ITEMS.register("doomicorn_helmet", () -> new DoomicornArmor( + ArmorItem.Type.HELMET)); + public static final RegistryObject DOOMICORN_CHESTPLATE = ITEMS.register("doomicorn_chestplate", () -> new DoomicornArmor( + ArmorItem.Type.CHESTPLATE)); + public static final RegistryObject DOOMICORN_LEGGINGS = ITEMS.register("doomicorn_leggings", () -> new DoomicornArmor( + ArmorItem.Type.LEGGINGS)); + public static final RegistryObject DOOMICORN_BOOTS = ITEMS.register("doomicorn_boots", () -> new DoomicornArmor( + ArmorItem.Type.BOOTS)); + } + + public void createEntityAttributes(final EntityAttributeCreationEvent event) { + event.put(AzureEntities.MARAUDER.get(), Monster.createMonsterAttributes().build()); + } + + public void commonSetup(final FMLCommonSetupEvent event) { + AzIdentityRegistry.register( + AzureItems.PISTOL_ITEM.get(), + AzureItems.DOOMICORN_HELMET.get(), + AzureItems.DOOMICORN_CHESTPLATE.get(), + AzureItems.DOOMICORN_LEGGINGS.get(), + AzureItems.DOOMICORN_BOOTS.get() + ); } } diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java b/neoforge/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java new file mode 100644 index 000000000..e8444277c --- /dev/null +++ b/neoforge/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java @@ -0,0 +1,52 @@ +package mod.azure.azurelib.testing.block; + +import mod.azure.azurelib.NeoForgeAzureLibMod; +import mod.azure.azurelib.testing.block.be.StargateBlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class StargateBlock extends BaseEntityBlock { + + public StargateBlock() { + super(Properties.of().sound(SoundType.DRIPSTONE_BLOCK).strength(5.0f, 8.0f).noOcclusion()); + } + + /** + * Creates a new {@link BlockEntity} instance for the Stargate block at the specified position and state. + * + * @param pos The position of the block in the world. + * @param state The current block state for this block entity. + * @return A new {@link BlockEntity} instance associated with the Stargate block, or {@code null} if none is + * available. + */ + @Override + public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos pos, @NotNull BlockState state) { + return NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get().create(pos, state); + } + + /** + * Determines the appropriate ticker for a block entity to handle its periodic updates. + * + * @param level The current level or world instance. + * @param state The block state of the associated block. + * @param type The type of the block entity to obtain the ticker for. + * @param A subtype of BlockEntity. + * @return A BlockEntityTicker for the specified block entity type, or null if no ticker is applicable. + */ + @Override + public BlockEntityTicker getTicker( + @NotNull Level level, + @NotNull BlockState state, + @NotNull BlockEntityType type + ) { + return createTickerHelper(type, NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), StargateBlockEntity::tick); + } +} diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java b/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java new file mode 100644 index 000000000..2ef220aac --- /dev/null +++ b/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java @@ -0,0 +1,26 @@ +package mod.azure.azurelib.testing.block.be; + +import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; +import mod.azure.azurelib.testing.CommonStrings; + +/** + * The StargateBlockAnimationDispatcher class is responsible for managing and triggering animation commands for block + * entities, specifically for the Stargate block entity. + */ +public class StargateBlockAnimationDispatcher { + + private static final AzCommand SPINNING_COMMAND = AzCommand.create( + CommonStrings.BASE_CONTROLLER, + CommonStrings.SPIN_ANIMATION_NAME + ); + + private final StargateBlockEntity stargateBlockEntity; + + public StargateBlockAnimationDispatcher(StargateBlockEntity stargateBlockEntity) { + this.stargateBlockEntity = stargateBlockEntity; + } + + public void serverSpin() { + SPINNING_COMMAND.sendForBlockEntity(stargateBlockEntity); + } +} diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java b/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java new file mode 100644 index 000000000..886fb372a --- /dev/null +++ b/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java @@ -0,0 +1,32 @@ +package mod.azure.azurelib.testing.block.be; + +import mod.azure.azurelib.NeoForgeAzureLibMod; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class StargateBlockEntity extends BlockEntity { + + public final StargateBlockAnimationDispatcher animationDispatcher; + + public StargateBlockEntity(BlockPos pos, BlockState blockState) { + super(NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), pos, blockState); + this.animationDispatcher = new StargateBlockAnimationDispatcher(this); + } + + /** + * Handles the tick behavior for the StargateBlockEntity, triggering server-side spinning animations if the block + * entity and level instance are valid and the method is executed on the client side. + * + * @param level The current level or world instance. + * @param pos The position of the block in the world. + * @param state The current block state of the associated block. + * @param blockEntity The StargateBlockEntity instance to operate on. + */ + public static void tick(Level level, BlockPos pos, BlockState state, StargateBlockEntity blockEntity) { + if (blockEntity.level != null && level.isClientSide()) { + blockEntity.animationDispatcher.serverSpin(); + } + } +} diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java b/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java new file mode 100644 index 000000000..17ef49324 --- /dev/null +++ b/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java @@ -0,0 +1,39 @@ +package mod.azure.azurelib.testing.block.be; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; +import mod.azure.azurelib.rewrite.animation.impl.AzBlockAnimator; +import mod.azure.azurelib.testing.block.be.StargateBlockEntity; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +/** + * StargateBlockEntityAnimator is responsible for managing and configuring animations for the StargateBlockEntity. It + * defines specific animations and registers them with the animation controller system, enabling dynamic and interactive + * visual effects based on the block entity's state. + */ +public class StargateBlockEntityAnimator extends AzBlockAnimator { + + private static final ResourceLocation ANIMATIONS = AzureLib.modResource( + "animations/block/stargate.animation.json" + ); + + protected StargateBlockEntityAnimator() { + super(AzAnimatorConfig.defaultConfig()); + } + + @Override + public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { + animationControllerContainer.add( + AzAnimationController.builder(this, "base_controller") + .build() + ); + } + + @Override + public @NotNull ResourceLocation getAnimationLocation(StargateBlockEntity animatable) { + return ANIMATIONS; + } +} diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java b/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java new file mode 100644 index 000000000..49be22d61 --- /dev/null +++ b/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java @@ -0,0 +1,21 @@ +package mod.azure.azurelib.testing.block.be; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRenderer; +import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRendererConfig; +import net.minecraft.resources.ResourceLocation; + +public class StargateBlockRenderer extends AzBlockEntityRenderer { + + private static final ResourceLocation MODEL = AzureLib.modResource("geo/block/stargate.geo.json"); + + private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/block/stargate.png"); + + public StargateBlockRenderer() { + super( + AzBlockEntityRendererConfig.builder(MODEL, TEXTURE) + .setAnimatorProvider(StargateBlockEntityAnimator::new) + .build() + ); + } +} From ee2157c5c98ac8a018a5d5501e2286f8dba04fa4 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:58:57 -0400 Subject: [PATCH 09/40] Fixes items not getting proper tag --- ...tackMixin_AzItemStackIdentityRegistry.java | 41 +++++++++++++++++-- .../AzIdentifiableItemStackAnimatorCache.java | 8 +++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java index e8898960a..d028abea7 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java @@ -3,8 +3,10 @@ import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; import mod.azure.azurelib.util.AzureLibUtil; import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; +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.CallbackInfo; @@ -30,9 +32,42 @@ public class ItemStackMixin_AzItemStackIdentityRegistry { at = @At("TAIL") ) public void az_addIdentityComponent(CompoundTag compoundTag, CallbackInfo ci) { - var self = AzureLibUtil.self(this); - if (AzIdentityRegistry.hasIdentity(self.getItem()) && !compoundTag.hasUUID("az_id")) { - compoundTag.putUUID("az_id", UUID.randomUUID()); + initializeAzIdOnStack(this, compoundTag); + } + + @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;)V", at = @At("TAIL")) + public void az_addIdentityComponentItemConstructor2(CallbackInfo ci) { + initializeAzIdOnStack(this, null); + } + + @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/core/Holder;)V", at = @At("TAIL")) + public void az_addIdentityComponentItemConstructor3(CallbackInfo ci) { + initializeAzIdOnStack(this, null); + } + + @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;ILjava/util/Optional;)V", at = @At("TAIL")) + public void az_addIdentityComponentItemConstructor4(CallbackInfo ci) { + initializeAzIdOnStack(this, null); + } + + @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;I)V", at = @At("TAIL")) + public void az_addIdentityComponentItemConstructor5(CallbackInfo ci) { + initializeAzIdOnStack(this, null); + } + + @Unique + private void initializeAzIdOnStack(Object stackObject, CompoundTag tag) { + var self = AzureLibUtil.self(stackObject); + + if (!self.hasTag()) { + self.setTag(new CompoundTag()); + } + + var stackTag = self.getTag(); + + if (stackTag != null && AzIdentityRegistry.hasIdentity(self.getItem()) && !stackTag.hasUUID("az_id")) { + stackTag.putUUID("az_id", UUID.randomUUID()); } } + } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java index d07913e51..220c63bfc 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java @@ -1,6 +1,7 @@ package mod.azure.azurelib.rewrite.animation.cache; import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; @@ -27,7 +28,12 @@ public static AzIdentifiableItemStackAnimatorCache getInstance() { private AzIdentifiableItemStackAnimatorCache() {} public void add(ItemStack itemStack, AzItemAnimator animator) { - var uuid = itemStack.getTag().getUUID("az_id"); + if (!itemStack.hasTag()) { + itemStack.setTag(new CompoundTag()); + } + + var tag = itemStack.getTag(); + var uuid = tag.getUUID("az_id"); if (uuid != null) { ANIMATORS_BY_UUID.computeIfAbsent(uuid, ($) -> animator); From f946104ed0b4d7cf638bf5b41bff43dc79f89f86 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Tue, 22 Apr 2025 21:37:12 -0400 Subject: [PATCH 10/40] Port back latest 3.x changes --- .../cache/texture/AnimatableTexture.java | 120 +++++++++++------- .../cache/texture/AutoGlowingTexture.java | 54 +++++++- .../azurelib/core/molang/MolangParser.java | 2 +- .../azurelib/core/molang/MolangQueries.java | 2 +- .../animation/easing/AzEasingTypes.java | 40 +++++- .../animation/easing/AzEasingUtil.java | 24 +++- .../parse/AzBakedAnimationsAdapter.java | 79 +++++++----- .../render/armor/AzArmorRendererPipeline.java | 5 +- .../block/AzBlockEntityRendererPipeline.java | 6 +- .../entity/AzEntityRendererPipeline.java | 5 +- .../render/item/AzItemRendererPipeline.java | 7 +- .../render/layer/AzAutoGlowingLayer.java | 14 ++ 12 files changed, 246 insertions(+), 112 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/cache/texture/AnimatableTexture.java b/common/src/main/java/mod/azure/azurelib/cache/texture/AnimatableTexture.java index 347d038b1..910a8fdba 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/texture/AnimatableTexture.java +++ b/common/src/main/java/mod/azure/azurelib/cache/texture/AnimatableTexture.java @@ -15,6 +15,7 @@ import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.util.RenderUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.AbstractTexture; import net.minecraft.client.renderer.texture.SimpleTexture; @@ -37,8 +38,9 @@ * Wrapper for {@link SimpleTexture SimpleTexture} implementation allowing for casual use of animated non-atlas textures */ public class AnimatableTexture extends SimpleTexture { - private AnimationContents animationContents = null; - private boolean isAnimated = false; + protected AnimationContents animationContents = null; + + protected boolean isAnimated = false; public AnimatableTexture(final ResourceLocation location) { super(location); @@ -47,33 +49,29 @@ public AnimatableTexture(final ResourceLocation location) { @Override public void load(ResourceManager manager) throws IOException { Resource resource = manager.getResourceOrThrow(this.location); + AnimationMetadataSection animMeta = resource.metadata().getSection(AnimationMetadataSection.SERIALIZER).orElse(null); - try { + if (animMeta != null) { NativeImage nativeImage; try (InputStream inputstream = resource.open()) { nativeImage = NativeImage.read(inputstream); } - this.animationContents = resource.metadata().getSection(AnimationMetadataSection.SERIALIZER).map(animMeta -> new AnimationContents(nativeImage, animMeta)).orElse(null); + this.animationContents = new AnimationContents(nativeImage, animMeta); - if (this.animationContents != null) { - if (!this.animationContents.isValid()) { - nativeImage.close(); + if (!this.animationContents.isValid()) { + nativeImage.close(); - return; - } + return; + } - this.isAnimated = true; + this.isAnimated = true; - onRenderThread(() -> { - TextureUtil.prepareImage(getId(), 0, this.animationContents.frameSize.width(), this.animationContents.frameSize.height()); - nativeImage.upload(0, 0, 0, 0, 0, this.animationContents.frameSize.width(), this.animationContents.frameSize.height(), false, false); - }); - } - } - catch (RuntimeException exception) { - AzureLib.LOGGER.warn("Failed reading metadata of: {}", this.location, exception); + onRenderThread(() -> { + TextureUtil.prepareImage(getId(), 0, this.animationContents.frameSize.width(), this.animationContents.frameSize.height()); + nativeImage.upload(0, 0, 0, 0, 0, this.animationContents.frameSize.width(), this.animationContents.frameSize.height(), false, false); + }); } } @@ -86,6 +84,10 @@ public boolean isAnimated() { return this.isAnimated; } + public static void setAndUpdate(ResourceLocation texturePath) { + setAndUpdate(texturePath, (int) RenderUtils.getCurrentTick()); + } + public static void setAndUpdate(ResourceLocation texturePath, int frameTick) { AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(texturePath); @@ -108,9 +110,9 @@ private static void onRenderThread(RenderCall renderCall) { } } - private class AnimationContents { - private final FrameSize frameSize; - private final Texture animatedTexture; + protected class AnimationContents { + protected final FrameSize frameSize; + protected final Texture animatedTexture; private AnimationContents(NativeImage image, AnimationMetadataSection animMeta) { this.frameSize = animMeta.calculateFrameSize(image.getWidth(), image.getHeight()); @@ -172,7 +174,7 @@ private Texture generateAnimatedTexture(NativeImage image, AnimationMetadataSect private record Frame(int index, int time) { } - private class Texture implements AutoCloseable { + public class Texture implements AutoCloseable { private final NativeImage baseImage; private final Frame[] frames; private final int framePanelSize; @@ -180,6 +182,10 @@ private class Texture implements AutoCloseable { private final NativeImage interpolatedFrame; private final int totalFrameTime; + protected int glowMaskTextureId = -1; + protected NativeImage glowmaskImage = null; + protected NativeImage glowmaskInterpolatedFrame = null; + private int currentFrame; private int currentSubframe; @@ -207,6 +213,13 @@ private int getFrameY(int frameIndex) { return frameIndex / this.framePanelSize; } + public void setGlowMaskTexture(AutoGlowingTexture texture, NativeImage baseImage, NativeImage glowMask) { + this.glowMaskTextureId = texture.getId(); + this.glowmaskImage = glowMask; + this.glowmaskInterpolatedFrame = this.interpolating ? new NativeImage(AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height(), false) : null; + this.baseImage.copyFrom(baseImage); + } + public void setCurrentFrame(int ticks) { ticks %= this.totalFrameTime; @@ -237,43 +250,49 @@ public void setCurrentFrame(int ticks) { getFrameY(this.currentFrame) * AnimationContents.this.frameSize.height(), AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height(), false, false); + + if (this.glowmaskImage != null) { + TextureUtil.prepareImage(this.glowMaskTextureId, 0, AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height()); + this.glowmaskImage.upload(0, 0, 0, getFrameX(this.currentFrame) * AnimationContents.this.frameSize.width(), getFrameY(this.currentFrame) * AnimationContents.this.frameSize.height(), AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height(), false, false); + } }); } else if (this.currentSubframe != lastSubframe && this.interpolating) { - onRenderThread(this::generateInterpolatedFrame); + onRenderThread(() -> { + generateInterpolatedFrame(getId(), this.baseImage, this.interpolatedFrame); + + if (this.glowmaskImage != null) { + generateInterpolatedFrame(this.glowMaskTextureId, this.glowmaskImage, + this.glowmaskInterpolatedFrame); + } + }); } } - private void generateInterpolatedFrame() { + private void generateInterpolatedFrame(int textureId, NativeImage image, NativeImage interpolatedFrame) { Frame frame = this.frames[this.currentFrame]; - double frameProgress = 1 - (double) this.currentSubframe / (double) frame.time; - int nextFrameIndex = this.frames[(this.currentFrame + 1) % this.frames.length].index; - - if (frame.index != nextFrameIndex) { - for (int y = 0; y < this.interpolatedFrame.getHeight(); ++y) { - for (int x = 0; x < this.interpolatedFrame.getWidth(); ++x) { - int prevFramePixel = getPixel(frame.index, x, y); - int nextFramePixel = getPixel(nextFrameIndex, x, y); - int blendedRed = interpolate(frameProgress, prevFramePixel >> 16 & 255, - nextFramePixel >> 16 & 255); - int blendedGreen = interpolate(frameProgress, prevFramePixel >> 8 & 255, - nextFramePixel >> 8 & 255); + double frameProgress = 1 - (double)this.currentSubframe / (double)frame.time(); + int nextFrameIndex = this.frames[(this.currentFrame + 1) % this.frames.length].index(); + + if (frame.index() != nextFrameIndex) { + for (int y = 0; y < interpolatedFrame.getHeight(); ++y) { + for (int x = 0; x < interpolatedFrame.getWidth(); ++x) { + int prevFramePixel = getPixel(image, frame.index(), x, y); + int nextFramePixel = getPixel(image, nextFrameIndex, x, y); + int blendedRed = interpolate(frameProgress, prevFramePixel >> 16 & 255, nextFramePixel >> 16 & 255); + int blendedGreen = interpolate(frameProgress, prevFramePixel >> 8 & 255, nextFramePixel >> 8 & 255); int blendedBlue = interpolate(frameProgress, prevFramePixel & 255, nextFramePixel & 255); - this.interpolatedFrame.setPixelRGBA(x, y, - prevFramePixel & -16777216 | blendedRed << 16 | blendedGreen << 8 | blendedBlue); + interpolatedFrame.setPixelRGBA(x, y, prevFramePixel & -16777216 | blendedRed << 16 | blendedGreen << 8 | blendedBlue); } } - TextureUtil.prepareImage(AnimatableTexture.this.getId(), 0, - AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height()); - this.interpolatedFrame.upload(0, 0, 0, 0, 0, AnimationContents.this.frameSize.width(), - AnimationContents.this.frameSize.height(), false, false); + TextureUtil.prepareImage(textureId, 0, AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height()); + interpolatedFrame.upload(0, 0, 0, 0, 0, AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height(), false, false); } } - private int getPixel(int frameIndex, int x, int y) { - return this.baseImage.getPixelRGBA(x + getFrameX(frameIndex) * AnimationContents.this.frameSize.width(), - y + getFrameY(frameIndex) * AnimationContents.this.frameSize.height()); + private int getPixel(NativeImage image, int frameIndex, int x, int y) { + return image.getPixelRGBA(x + getFrameX(frameIndex) * AnimationContents.this.frameSize.width(), y + getFrameY(frameIndex) * AnimationContents.this.frameSize.height()); } private int interpolate(double frameProgress, double prevColour, double nextColour) { @@ -284,8 +303,17 @@ private int interpolate(double frameProgress, double prevColour, double nextColo public void close() { this.baseImage.close(); - if (this.interpolatedFrame != null) + if (this.interpolatedFrame != null) { this.interpolatedFrame.close(); + } + + if (this.glowmaskImage != null) { + this.glowmaskImage.close(); + } + + if (this.glowmaskInterpolatedFrame != null) { + this.glowmaskInterpolatedFrame.close(); + } } } } diff --git a/common/src/main/java/mod/azure/azurelib/cache/texture/AutoGlowingTexture.java b/common/src/main/java/mod/azure/azurelib/cache/texture/AutoGlowingTexture.java index 012f561d7..92818b59c 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/texture/AutoGlowingTexture.java +++ b/common/src/main/java/mod/azure/azurelib/cache/texture/AutoGlowingTexture.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.util.Optional; import java.util.concurrent.ExecutionException; +import java.util.function.BiFunction; import java.util.function.Function; /** @@ -47,11 +48,31 @@ public class AutoGlowingTexture extends GeoAbstractTexture { RenderSystem.defaultBlendFunc(); }); private static final RenderStateShard.WriteMaskStateShard WRITE_MASK = new RenderStateShard.WriteMaskStateShard(true, true); - private static final Function RENDER_TYPE_FUNCTION = Util.memoize(texture -> { - RenderStateShard.TextureStateShard textureState = new RenderStateShard.TextureStateShard(texture, false, false); - return RenderType.create("geo_glowing_layer", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, false, true, RenderType.CompositeState.builder().setShaderState(SHADER_STATE).setTextureState(textureState).setTransparencyState(TRANSPARENCY_STATE).setWriteMaskState(WRITE_MASK).createCompositeState(false)); - }); + protected static final BiFunction GLOWING_RENDER_TYPE = Util.memoize( + (texture, isGlowing) -> { + RenderStateShard.TextureStateShard textureState = new RenderStateShard.TextureStateShard( + texture, + false, + false + ); + + return RenderType.create( + "az_glowing_layer", + DefaultVertexFormat.NEW_ENTITY, + VertexFormat.Mode.QUADS, + 256, + false, + true, + RenderType.CompositeState.builder() + .setShaderState(SHADER_STATE) + .setTextureState(textureState) + .setTransparencyState(TRANSPARENCY_STATE) + .setWriteMaskState(WRITE_MASK) + .createCompositeState(isGlowing) + ); + } + ); private static final String APPENDIX = "_glowmask"; @@ -128,11 +149,20 @@ protected RenderCall loadTexture(ResourceManager resourceManager, Minecraft mc) NativeImage mask = glowImage; - if (mask == null) + if (mask == null) { + String expectedGlowmask = this.textureBase.toString().replace(".png", "_glowmask.png"); + AzureLib.LOGGER.warn("Missing glowmask texture. Base texture: {}, Expected glowmask: {}", this.textureBase, expectedGlowmask); return null; + } + + boolean animated = originalTexture instanceof AnimatableTexture animatableTexture && animatableTexture.isAnimated(); + + if (animated) + ((AnimatableTexture)originalTexture).animationContents.animatedTexture.setGlowMaskTexture(this, baseImage, mask); return () -> { - uploadSimple(getId(), mask, blur, clamp); + if (!animated) + uploadSimple(getId(), mask, blur, clamp); if (originalTexture instanceof DynamicTexture dynamicTexture) { dynamicTexture.upload(); @@ -148,6 +178,16 @@ protected RenderCall loadTexture(ResourceManager resourceManager, Minecraft mc) * @param texture The texture of the resource to apply a glow layer to */ public static RenderType getRenderType(ResourceLocation texture) { - return RENDER_TYPE_FUNCTION.apply(getEmissiveResource(texture)); + return GLOWING_RENDER_TYPE.apply(getEmissiveResource(texture), false); + } + + /** + * Return a cached instance of the RenderType for the given texture for AutoGlowingGeoLayer rendering, while the + * entity has an outline + * + * @param texture The texture of the resource to apply a glow layer to + */ + public static RenderType getOutlineRenderType(ResourceLocation texture) { + return GLOWING_RENDER_TYPE.apply(getEmissiveResource(texture), true); } } diff --git a/common/src/main/java/mod/azure/azurelib/core/molang/MolangParser.java b/common/src/main/java/mod/azure/azurelib/core/molang/MolangParser.java index 65437bc5c..435c83902 100644 --- a/common/src/main/java/mod/azure/azurelib/core/molang/MolangParser.java +++ b/common/src/main/java/mod/azure/azurelib/core/molang/MolangParser.java @@ -39,7 +39,7 @@ public class MolangParser extends MathBuilder { public static final MolangParser INSTANCE = new MolangParser(); - private MolangParser() { + public MolangParser() { super(); // Remap functions to be intact with Molang specification diff --git a/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java b/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java index 4552ec98c..9e694e225 100644 --- a/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java +++ b/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java @@ -45,7 +45,7 @@ public final class MolangQueries { public static final String YAW_SPEED = normalize("query.yaw_speed"); - private static String normalize(String queryName) { + public static String normalize(String queryName) { if (queryName.startsWith(QUERY_PREFIX)) { return queryName; } else if (queryName.startsWith(SHORT_PREFIX)) { diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypes.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypes.java index 2854d0eea..3d82506c5 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypes.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypes.java @@ -1,5 +1,9 @@ package mod.azure.azurelib.rewrite.animation.easing; +import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; +import mod.azure.azurelib.core.utils.Interpolations; +import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzAnimationPoint; + public class AzEasingTypes { public static final AzEasingType NONE = AzEasingTypeRegistry.register( @@ -166,7 +170,41 @@ public class AzEasingTypes { public static final AzEasingType CATMULLROM = AzEasingTypeRegistry.register( "catmullrom", - value -> AzEasingUtil.easeInOut(AzEasingUtil::catmullRom) + new AzEasingType() { + @Override + public String name() { + return "Catmull-Rom"; + } + + @Override + public Double2DoubleFunction buildTransformer(Double value) { + return AzEasingUtil.easeInOut(AzEasingUtil::catmullRom); + } + + @Override + public double apply(AzAnimationPoint animationPoint, Double easingValue, double lerpValue) { + if (animationPoint.currentTick() >= animationPoint.transitionLength()) { + return animationPoint.animationEndValue(); + } + + var easingArgs = animationPoint.keyframe().easingArgs(); + + if (easingArgs.size() < 2) + return Interpolations.lerp( + buildTransformer(easingValue).apply(lerpValue), + animationPoint.animationStartValue(), + animationPoint.animationEndValue() + ); + + return AzEasingUtil.catmullRom( + lerpValue, + easingArgs.get(0).get(), + animationPoint.animationStartValue(), + animationPoint.animationEndValue(), + easingArgs.get(1).get() + ); + } + } ); public static AzEasingType random() { diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingUtil.java index bd29375ab..209892aab 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingUtil.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingUtil.java @@ -15,11 +15,27 @@ public static Double2DoubleFunction linear(Double2DoubleFunction function) { /** * Performs a Catmull-Rom interpolation, used to get smooth interpolated motion between keyframes.
*
CatmullRom#position + * + * @param delta The interpolation parameter (between 0 and 1) + * @param p0 First control point (anchor) + * @param p1 Second control point (start point) + * @param p2 Third control point (end point) + * @param p3 Fourth control point (anchor) + * @return The interpolated value + */ + public static double catmullRom(double delta, double p0, double p1, double p2, double p3) { + return 0.5d * (2d * p1 + (p2 - p0) * delta + + (2d * p0 - 5d * p1 + 4d * p2 - p3) * delta * delta + + (3d * p1 - p0 - 3d * p2 + p3) * delta * delta * delta); + } + + /** + * Simplified Catmull-Rom interpolation for single parameter + * @param n The interpolation parameter + * @return The interpolated value */ - public static double catmullRom(double n) { - return (0.5f * (2.0f * (n + 1) + ((n + 2) - n) * 1 - + (2.0f * n - 5.0f * (n + 1) + 4.0f * (n + 2) - (n + 3)) * 1 - + (3.0f * (n + 1) - n - 3.0f * (n + 2) + (n + 3)) * 1)); + public static double catmullRom(double n) {// Using default control points for simple interpolation + return catmullRom(n, 0, 0, 1, 1); } /** diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzBakedAnimationsAdapter.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzBakedAnimationsAdapter.java index b54d517f6..f2420c4b6 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzBakedAnimationsAdapter.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzBakedAnimationsAdapter.java @@ -50,7 +50,7 @@ public class AzBakedAnimationsAdapter implements JsonDeserializer> getTripletObj(JsonElement element) { + private static List> getKeyframes(JsonElement element) { if (element == null) return List.of(); @@ -68,16 +68,24 @@ private static List> getTripletObj(JsonElement element return ObjectArrayList.of(Pair.of("0", array)); if (element instanceof JsonObject obj) { + if (obj.has("vector")) + return ObjectArrayList.of(Pair.of("0", obj)); + List> list = new ObjectArrayList<>(); for (Map.Entry entry : obj.entrySet()) { + double timestamp = readTimestamp(entry.getKey()); + + if (timestamp == 0 && !list.isEmpty()) + throw new JsonParseException("Invalid keyframe data - multiple starting keyframes?" + entry.getKey()); + if (entry.getValue() instanceof JsonObject entryObj && !entryObj.has("vector")) { - list.add(getTripletObjBedrock(entry.getKey(), entryObj)); + addBedrockKeyframes(timestamp, entryObj, list); continue; } - list.add(Pair.of(entry.getKey(), entry.getValue())); + list.add(Pair.of(String.valueOf(timestamp), entry.getValue())); } return list; @@ -86,39 +94,40 @@ private static List> getTripletObj(JsonElement element throw new JsonParseException("Invalid object type provided to getTripletObj, got: " + element); } - /** - * Extracts and processes keyframe data from a given JSON object, returning a pair consisting of a timestamp and - * associated JSON element data. The method focuses on retrieving either the "pre" or "post" keyframe data from the - * input JSON object, applying specific handling for array or object-based representations. - * - * @param timestamp The string representation of the timestamp for the keyframe data. If the input value is not - * valid as a numeric string, it defaults to "0". - * @param keyframe A {@link JsonObject} containing the keyframe data. Expected keys include "pre" or "post" with - * their associated values either as JSON arrays or nested objects containing a "vector" element. - * @return A {@link Pair} where the first element is the processed timestamp as a string, and the second element is - * a {@link JsonArray} representing the keyframe values. - * @throws JsonParseException If the provided keyframe data is invalid or does not meet the expected structure, such - * as missing or incorrectly formatted "pre" or "post" keys. - */ - private static Pair getTripletObjBedrock(String timestamp, JsonObject keyframe) { - JsonArray keyframeValues = null; + private static void addBedrockKeyframes(double timestamp, JsonObject keyframe, List> keyframes) { + boolean addedFrame = false; if (keyframe.has("pre")) { JsonElement pre = keyframe.get("pre"); - keyframeValues = pre.isJsonArray() - ? pre.getAsJsonArray() - : GsonHelper.getAsJsonArray(pre.getAsJsonObject(), "vector"); - } else if (keyframe.has("post")) { - JsonElement post = keyframe.get("post"); - keyframeValues = post.isJsonArray() - ? post.getAsJsonArray() - : GsonHelper.getAsJsonArray(post.getAsJsonObject(), "vector"); + addedFrame = true; + + keyframes.add(Pair.of( + String.valueOf(timestamp == 0 ? timestamp : timestamp - 0.001d), + pre.isJsonArray() ? pre.getAsJsonArray() : GsonHelper.getAsJsonArray(pre.getAsJsonObject(), "vector") + )); } - if (keyframeValues != null) - return Pair.of(NumberUtils.isCreatable(timestamp) ? timestamp : "0", keyframeValues); + if (keyframe.has("post")) { + JsonElement post = keyframe.get("post"); + JsonArray values = post.isJsonArray() ? post.getAsJsonArray() : GsonHelper.getAsJsonArray(post.getAsJsonObject(), "vector"); + + if (keyframe.has("lerp_mode")) { + var keyframeObj = new JsonObject(); + + keyframeObj.add("vector", values); + keyframeObj.add("easing", keyframe.get("lerp_mode")); + + keyframes.add(Pair.of(String.valueOf(timestamp), keyframeObj)); + } + else { + keyframes.add(Pair.of(String.valueOf(timestamp), values)); + } - throw new JsonParseException("Invalid keyframe data - expected array, found " + keyframe); + return; + } + + if (!addedFrame) + throw new JsonParseException("Invalid keyframe data - expected array, found " + keyframe); } /** @@ -254,15 +263,15 @@ private AzBoneAnimation[] bakeBoneAnimations(JsonObject bonesObj) throws MolangE for (Map.Entry entry : bonesObj.entrySet()) { JsonObject entryObj = entry.getValue().getAsJsonObject(); AzKeyframeStack> scaleFrames = buildKeyframeStack( - getTripletObj(entryObj.get("scale")), + getKeyframes(entryObj.get("scale")), false ); AzKeyframeStack> positionFrames = buildKeyframeStack( - getTripletObj(entryObj.get("position")), + getKeyframes(entryObj.get("position")), false ); AzKeyframeStack> rotationFrames = buildKeyframeStack( - getTripletObj(entryObj.get("rotation")), + getKeyframes(entryObj.get("rotation")), true ); @@ -361,4 +370,8 @@ private AzKeyframeStack> buildKeyframeStack( return new AzKeyframeStack<>(xFrames, yFrames, zFrames); } + + private static double readTimestamp(String timestamp) { + return NumberUtils.isCreatable(timestamp) ? Double.parseDouble(timestamp) : 0; + } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java index 21392acd6..764c54bb3 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java @@ -42,10 +42,7 @@ protected void updateAnimatedTextureFrame(ItemStack animatable) { var currentEntity = context().currentEntity(); if (currentEntity != null) { - var textureLocation = config().textureLocation(animatable); - var frameTick = currentEntity.getId() + currentEntity.tickCount; - - AnimatableTexture.setAndUpdate(textureLocation, frameTick); + AnimatableTexture.setAndUpdate(config.textureLocation(animatable)); } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java index 7ed9c24ad..f22eae844 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java @@ -55,11 +55,7 @@ protected AzLayerRenderer createLayerRenderer(AzRendererConfig config) { */ @Override public void updateAnimatedTextureFrame(T entity) { - AnimatableTexture.setAndUpdate( - config.textureLocation(entity), - entity.getBlockPos().getX() + entity.getBlockPos().getY() + entity.getBlockPos().getZ() - + (int) RenderUtils.getCurrentTick() - ); + AnimatableTexture.setAndUpdate(config.textureLocation(entity)); } /** diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java index db87c9ea8..a0734b850 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java @@ -52,10 +52,7 @@ protected AzLayerRenderer createLayerRenderer(AzRendererConfig config) { */ @Override public void updateAnimatedTextureFrame(T entity) { - AnimatableTexture.setAndUpdate( - config.textureLocation(entity), - entity.getId() + entity.tickCount - ); + AnimatableTexture.setAndUpdate(config.textureLocation(entity)); } /** diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java index 05171d359..b6b49e174 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java @@ -6,9 +6,7 @@ import mod.azure.azurelib.rewrite.render.AzRendererConfig; import mod.azure.azurelib.rewrite.render.AzRendererPipeline; import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; -import mod.azure.azurelib.util.RenderUtils; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import org.joml.Matrix4f; @@ -81,10 +79,7 @@ public void postRender(AzRendererPipelineContext context, boolean isR */ @Override public void updateAnimatedTextureFrame(ItemStack animatable) { - AnimatableTexture.setAndUpdate( - config.textureLocation(animatable), - Item.getId(animatable.getItem()) + (int) RenderUtils.getCurrentTick() - ); + AnimatableTexture.setAndUpdate(config.textureLocation(animatable)); } public AzItemRenderer getRenderer() { diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzAutoGlowingLayer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzAutoGlowingLayer.java index 97f5e081e..a2c9ce681 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzAutoGlowingLayer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzAutoGlowingLayer.java @@ -3,7 +3,10 @@ import mod.azure.azurelib.cache.texture.AutoGlowingTexture; import mod.azure.azurelib.rewrite.model.AzBone; import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; +import mod.azure.azurelib.util.ClientUtils; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderType; +import net.minecraft.world.entity.Entity; /** * A {@link AzRenderLayer} dedicated to rendering the auto-generated glow layer functionality provided by AzureLib. This @@ -31,6 +34,17 @@ public void render(AzRendererPipelineContext context) { var textureLocation = renderPipeline.config().textureLocation(animatable); var renderType = AutoGlowingTexture.getRenderType(textureLocation); + if (context.animatable() instanceof Entity entity) { + var isInvisibleButVisibleToPlayer = entity.isInvisible() && !entity.isInvisibleTo(ClientUtils.getClientPlayer()); + var shouldAppearGlowing = Minecraft.getInstance().shouldEntityAppearGlowing(entity); + + if (isInvisibleButVisibleToPlayer) { + renderType = RenderType.outline(textureLocation); + } else if (shouldAppearGlowing) { + renderType = AutoGlowingTexture.getOutlineRenderType(textureLocation); + } + } + if (context.renderType() != null) { var prevRenderType = context.renderType(); var prevPackedLight = context.packedLight(); From 2b3e6450f27894310626d550ff4f44b2ce1cc9fc Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 28 Apr 2025 02:15:53 -0400 Subject: [PATCH 11/40] Update tooling for discord posting of updates --- fabric/build.gradle | 14 +++++++++++++- neoforge/build.gradle | 18 +++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/fabric/build.gradle b/fabric/build.gradle index 2c0ee03c1..0e7e4723a 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -6,7 +6,7 @@ plugins { id 'idea' id 'maven-publish' id 'fabric-loom' - id "me.modmuss50.mod-publish-plugin" version "0.4.0" + id "me.modmuss50.mod-publish-plugin" version "0.8.4" } base { @@ -131,6 +131,18 @@ if (file('key.properties').exists()) { modLoaders.add("fabric") modLoaders.add("quilt") + discord { + webhookUrl = releaseProp.getProperty("discordKey") + dryRunWebhookUrl = releaseProp.getProperty("discordKey") + username = "AzureBot" + avatarUrl = "https://cdn.modrinth.com/data/7zlUOZvb/8b2cb16452c1d3bb33574519a6f5cf4ce86f13a3_96.webp" + content = changelog.map { "# A new version of AzureLib for Fabric has been released for 1.20.1 \n" + it} + setPlatforms(publishMods.platforms.curseforge, publishMods.platforms.modrinth) + style { + look = "MODERN" + thumbnailUrl = "https://cdn.modrinth.com/data/7zlUOZvb/8b2cb16452c1d3bb33574519a6f5cf4ce86f13a3_96.webp" + } + } curseforge { projectId = "817423" projectSlug = "azurelib" diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 99c69d050..631131bad 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -3,7 +3,7 @@ plugins { id 'maven-publish' id 'net.minecraftforge.gradle' id 'org.spongepowered.mixin' - id "me.modmuss50.mod-publish-plugin" version "0.4.0" + id "me.modmuss50.mod-publish-plugin" version "0.8.4" } base { @@ -143,16 +143,28 @@ if (file('key.properties').exists()) { modLoaders.add("neoforge") modLoaders.add("forge") + discord { + webhookUrl = releaseProp.getProperty("discordKey") + dryRunWebhookUrl = releaseProp.getProperty("discordKey") + username = "AzureBot" + avatarUrl = "https://cdn.modrinth.com/data/7zlUOZvb/8b2cb16452c1d3bb33574519a6f5cf4ce86f13a3_96.webp" + content = changelog.map { "# A new version of AzureLib for Forge has been released for 1.20.1 \n" + it} + setPlatforms(publishMods.platforms.curseforge, publishMods.platforms.modrinth) + style { + look = "MODERN" + thumbnailUrl = "https://cdn.modrinth.com/data/7zlUOZvb/8b2cb16452c1d3bb33574519a6f5cf4ce86f13a3_96.webp" + } + } curseforge { projectId = "817423" projectSlug = "azurelib" accessToken = releaseProp.getProperty("curseKey") - minecraftVersions.add("1.20.1") + minecraftVersions.add(project.minecraft_version) } modrinth { projectId = "7zlUOZvb" accessToken = releaseProp.getProperty('modrinthKey') - minecraftVersions.add("1.20.1") + minecraftVersions.add(project.minecraft_version) } } } \ No newline at end of file From 121f4ae1606338cbe1af5c7309565fdb82351791 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 28 Apr 2025 16:15:08 -0400 Subject: [PATCH 12/40] Fixes black rendering --- .../rewrite/render/AzRendererPipeline.java | 9 +++---- .../render/AzRendererPipelineContext.java | 24 ++++++++++++------- .../rewrite/render/armor/AzArmorModel.java | 2 +- .../render/block/AzBlockEntityRenderer.java | 2 +- .../render/entity/AzEntityRenderer.java | 7 +----- .../render/item/AzItemGuiRenderUtil.java | 3 ++- .../rewrite/render/item/AzItemRenderer.java | 3 ++- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipeline.java index afcc0bc47..1b1bba049 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipeline.java @@ -3,6 +3,7 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import mod.azure.azurelib.cache.texture.AnimatableTexture; +import mod.azure.azurelib.core.object.Color; import mod.azure.azurelib.rewrite.model.AzBakedModel; import mod.azure.azurelib.rewrite.render.layer.AzRenderLayer; import net.minecraft.client.renderer.MultiBufferSource; @@ -89,14 +90,10 @@ public void render( @Nullable VertexConsumer buffer, float yaw, float partialTick, - int packedLight, - float red, - float green, - float blue, - float alpha + int packedLight ) { renderType = config.getRenderType(animatable); - context.populate(animatable, model, bufferSource, packedLight, partialTick, poseStack, renderType, buffer, red, blue, green, alpha); + context.populate(animatable, model, bufferSource, packedLight, partialTick, poseStack, renderType, buffer); poseStack.pushPose(); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java index 25c4ec0a7..0778fc933 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java @@ -2,6 +2,7 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; +import mod.azure.azurelib.core.object.Color; import mod.azure.azurelib.rewrite.model.AzBakedModel; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; @@ -74,11 +75,7 @@ public void populate( float partialTick, PoseStack poseStack, RenderType renderType, - VertexConsumer vertexConsumer, - float red, - float green, - float blue, - float alpha + VertexConsumer vertexConsumer ) { this.animatable = animatable; this.bakedModel = bakedModel; @@ -89,10 +86,11 @@ public void populate( this.poseStack = poseStack; this.renderType = renderType; this.vertexConsumer = vertexConsumer; - this.red = red; - this.green = green; - this.blue = blue; - this.alpha = alpha; + Color renderColor = getRenderColor(animatable, partialTick, packedLight); + this.red = renderColor.getRedFloat(); + this.green = renderColor.getGreenFloat(); + this.blue = renderColor.getBlueFloat(); + this.alpha = renderColor.getAlphaFloat(); if (renderType == null) { var textureLocation = rendererPipeline.config().textureLocation(animatable); @@ -118,6 +116,14 @@ public abstract RenderType getDefaultRenderType( float partialTick ); + /** + * Gets a tint-applying color to render the given animatable with.
+ * Returns {@link Color#WHITE} by default + */ + public Color getRenderColor(T animatable, float partialTick, int packedLight) { + return Color.WHITE; + } + /** * Gets a packed overlay coordinate pair for rendering.
* Mostly just used for the red tint when an entity is hurt, but can be used for other things like the diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModel.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModel.java index d2420e9a2..f937b1e4b 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModel.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModel.java @@ -54,7 +54,7 @@ public void renderToBuffer( buffer = ItemRenderer.getArmorFoilBuffer(bufferSource, renderType, false, currentStack.hasFoil()); var model = rendererPipeline.renderer().provider().provideBakedModel(animatable); - rendererPipeline.render(poseStack, model, animatable, bufferSource, null, buffer, 0, partialTick, packedLight, red, green, blue, alpha); + rendererPipeline.render(poseStack, model, animatable, bufferSource, null, buffer, 0, partialTick, packedLight); } /** diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRenderer.java index 687f6383d..833019e30 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRenderer.java @@ -55,7 +55,7 @@ public void render( reusedAzBlockAnimator = cachedEntityAnimator; // Execute the render pipeline. - rendererPipeline.render(poseStack, model, entity, source, null, null, 0, partialTick, packedLight, context.red(), context.green(), context.blue(), context.alpha()); + rendererPipeline.render(poseStack, model, entity, source, null, null, 0, partialTick, packedLight); } public AzBlockAnimator getAnimator() { diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java index 2be472e15..b3699c9c2 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java @@ -71,7 +71,6 @@ public void render( ) { var cachedEntityAnimator = (AzEntityAnimator) provider.provideAnimator(entity); var azBakedModel = provider.provideBakedModel(entity); - var context = rendererPipeline.context(); if (cachedEntityAnimator != null && azBakedModel != null) { cachedEntityAnimator.setActiveModel(azBakedModel); @@ -90,11 +89,7 @@ public void render( null, entityYaw, partialTick, - packedLight, - context.red(), - context.green(), - context.blue(), - context.alpha() + packedLight ); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemGuiRenderUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemGuiRenderUtil.java index 3f39dd0e3..b7cb8da6b 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemGuiRenderUtil.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemGuiRenderUtil.java @@ -3,6 +3,7 @@ import com.mojang.blaze3d.platform.Lighting; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.core.object.Color; import mod.azure.azurelib.rewrite.model.AzBakedModel; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; @@ -45,7 +46,7 @@ public static void renderInGui( poseStack.pushPose(); - rendererPipeline.render(poseStack, model, stack, bSource, renderType, buffer, 0, partialTick, packedLight, 1, 1, 1, 1); + rendererPipeline.render(poseStack, model, stack, bSource, renderType, buffer, 0, partialTick, packedLight); bSource.endBatch(); RenderSystem.enableDepthTest(); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java index e928e0390..f5441ca6d 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java @@ -1,6 +1,7 @@ package mod.azure.azurelib.rewrite.render.item; import com.mojang.blaze3d.vertex.PoseStack; +import mod.azure.azurelib.core.object.Color; import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; import mod.azure.azurelib.rewrite.model.AzBakedModel; import mod.azure.azurelib.rewrite.render.AzProvider; @@ -72,7 +73,7 @@ public void renderByItem( prepareAnimator(stack, model); - rendererPipeline.render(poseStack, model, stack, source, renderType, buffer, 0, partialTick, packedLight, 1, 1, 1, 1); + rendererPipeline.render(poseStack, model, stack, source, renderType, buffer, 0, partialTick, packedLight); } private void prepareAnimator(ItemStack stack, AzBakedModel model) { From 8a864dc0e0de4771eb7217d9460302b045e08ad2 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 28 Apr 2025 16:15:43 -0400 Subject: [PATCH 13/40] Update AzEntityModelRenderer.java --- .../render/entity/AzEntityModelRenderer.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityModelRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityModelRenderer.java index 9f693aab7..104161d2a 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityModelRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityModelRenderer.java @@ -117,27 +117,6 @@ public void render(AzRendererPipelineContext context, boolean isReRender) { } if (!isReRender) { - // FIXME: Figure out what to do with this data stuff. - // float headPitch = Mth.lerp(partialTick, animatable.xRotO, animatable.getXRot()); - // var velocity = animatable.getDeltaMovement(); - // float avgVelocity = (float) (Math.abs(velocity.x) + Math.abs(velocity.z) / 2f); - // - // long instanceId = getInstanceId(animatable); - // - // animationState.setData(DataTickets.TICK, animatable.getTick(animatable)); - // animationState.setData(DataTickets.ENTITY, animatable); - // animationState.setData( - // DataTickets.ENTITY_MODEL_DATA, - // new EntityModelData( - // shouldSit, - // livingEntity != null && livingEntity.isBaby(), - // -netHeadYaw, - // -headPitch - // ) - // ); - // - // this.model.addAdditionalStateData(animatable, instanceId, animationState::setData); - var animator = entityRendererPipeline.getRenderer().getAnimator(); if (animator != null) { From 0ab322c5fbd984d737d991b69c0fa19f361d3d40 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Mon, 28 Apr 2025 16:17:03 -0400 Subject: [PATCH 14/40] Part 1 of StreamCodec backporting, still getting nulls for some reason. --- ...tackMixin_AzItemStackIdentityRegistry.java | 13 +-- .../AzBlockEntityDispatchCommandPacket.java | 21 +--- .../packet/AzEntityDispatchCommandPacket.java | 22 +--- .../AzItemStackDispatchCommandPacket.java | 23 +--- .../keyframe/AzKeyframeExecutor.java | 37 +++--- .../animation/dispatch/AzDispatchSide.java | 14 ++- .../animation/dispatch/command/AzCommand.java | 20 +++- .../dispatch/command/action/AzAction.java | 17 ++- .../command/action/codec/AzActionCodec.java | 39 ++++--- .../action/impl/root/AzRootCancelAction.java | 24 +++- .../impl/root/AzRootCancelAllAction.java | 16 ++- .../AzRootPlayAnimationSequenceAction.java | 28 +++-- .../root/AzRootSetAnimationSpeedAction.java | 24 +++- .../impl/root/AzRootSetEasingTypeAction.java | 25 ++++- .../root/AzRootSetTransitionSpeedAction.java | 24 +++- .../action/registry/AzActionRegistry.java | 106 +++++++++++++----- .../command/sequence/AzAnimationSequence.java | 19 +++- .../command/stage/AzAnimationStage.java | 21 ++-- .../animation/easing/AzEasingType.java | 10 +- .../property/AzAnimationProperties.java | 7 +- .../property/AzAnimationStageProperties.java | 7 +- .../codec/AzAnimationPropertiesCodec.java | 16 +-- .../AzAnimationStagePropertiesCodec.java | 15 +-- .../rewrite/util/codec/AzListStreamCodec.java | 25 +++-- 24 files changed, 369 insertions(+), 204 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java index d028abea7..be4bd8fba 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java @@ -3,7 +3,6 @@ import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; import mod.azure.azurelib.util.AzureLibUtil; import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @@ -32,31 +31,31 @@ public class ItemStackMixin_AzItemStackIdentityRegistry { at = @At("TAIL") ) public void az_addIdentityComponent(CompoundTag compoundTag, CallbackInfo ci) { - initializeAzIdOnStack(this, compoundTag); + azureLib$initializeAzIdOnStack(this, compoundTag); } @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;)V", at = @At("TAIL")) public void az_addIdentityComponentItemConstructor2(CallbackInfo ci) { - initializeAzIdOnStack(this, null); + azureLib$initializeAzIdOnStack(this, null); } @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/core/Holder;)V", at = @At("TAIL")) public void az_addIdentityComponentItemConstructor3(CallbackInfo ci) { - initializeAzIdOnStack(this, null); + azureLib$initializeAzIdOnStack(this, null); } @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;ILjava/util/Optional;)V", at = @At("TAIL")) public void az_addIdentityComponentItemConstructor4(CallbackInfo ci) { - initializeAzIdOnStack(this, null); + azureLib$initializeAzIdOnStack(this, null); } @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;I)V", at = @At("TAIL")) public void az_addIdentityComponentItemConstructor5(CallbackInfo ci) { - initializeAzIdOnStack(this, null); + azureLib$initializeAzIdOnStack(this, null); } @Unique - private void initializeAzIdOnStack(Object stackObject, CompoundTag tag) { + private void azureLib$initializeAzIdOnStack(Object stackObject, CompoundTag tag) { var self = AzureLibUtil.self(stackObject); if (!self.hasTag()) { diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java index bc487ee73..66f798cba 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java @@ -12,16 +12,8 @@ public class AzBlockEntityDispatchCommandPacket extends AbstractPacket { - // TODO: Updated encode/receive methods for AzCommand.CODEC/dispatchCommand - public static final StreamCodec CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, - AzBlockEntityDispatchCommandPacket::blockPos, - AzCommand.CODEC, - AzBlockEntityDispatchCommandPacket::dispatchCommand, - AzBlockEntityDispatchCommandPacket::new - ); - private final BlockPos blockPos; + private final AzCommand dispatchCommand; public AzBlockEntityDispatchCommandPacket( @@ -35,8 +27,7 @@ public AzBlockEntityDispatchCommandPacket( @Override public void encode(FriendlyByteBuf buf) { buf.writeBlockPos(this.blockPos); - // TODO: Needs fixed - // AzCommand.CODEC + AzCommand.ENCODER.accept(buf, this.dispatchCommand); } @Override @@ -45,11 +36,9 @@ public ResourceLocation getPacketID() { } public static AzBlockEntityDispatchCommandPacket receive(FriendlyByteBuf buf) { - var pos = buf.readBlockPos(); - // TODO: Needs fixed - // AzCommand azCommand = buf.readUtf(); - - return new AzBlockEntityDispatchCommandPacket(pos, azCommand); + BlockPos blockPos = buf.readBlockPos(); // Decode block position + AzCommand dispatchCommand = AzCommand.DECODER.apply(buf); // Decode AzCommand + return new AzBlockEntityDispatchCommandPacket(blockPos, dispatchCommand); // Create a new packet instance } @Override diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java index 513f85771..7509249d1 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java @@ -11,15 +11,6 @@ public class AzEntityDispatchCommandPacket extends AbstractPacket { - // TODO: Updated encode/receive methods for AzCommand.CODEC/dispatchCommand - public static final StreamCodec CODEC = StreamCodec.composite( - ByteBufCodecs.VAR_INT, - AzEntityDispatchCommandPacket::entityId, - AzCommand.CODEC, - AzEntityDispatchCommandPacket::dispatchCommand, - AzEntityDispatchCommandPacket::new - ); - private final int entityId; private final AzCommand dispatchCommand; @@ -34,8 +25,7 @@ public AzEntityDispatchCommandPacket( @Override public void encode(FriendlyByteBuf buf) { buf.writeInt(this.entityId); - // TODO: Needs fixed - // AzCommand.CODEC + AzCommand.ENCODER.accept(buf, this.dispatchCommand); } @Override @@ -43,12 +33,10 @@ public ResourceLocation getPacketID() { return AzureLibNetwork.AZ_ENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID; } - public static AzItemStackDispatchCommandPacket receive(FriendlyByteBuf buf) { - var entityId = buf.readInt(); - // TODO: Needs fixed - // AzCommand azCommand = buf.readUtf(); - - return new AzItemStackDispatchCommandPacket(entityId, azCommand); + public static AzEntityDispatchCommandPacket receive(FriendlyByteBuf buf) { + int entityId = buf.readInt(); // Decode integer entity ID + AzCommand dispatchCommand = AzCommand.DECODER.apply(buf); // Decode AzCommand + return new AzEntityDispatchCommandPacket(entityId, dispatchCommand); // Create and return the packet instance } public void handle() { diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java index 7f239d3e4..720b148c4 100644 --- a/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java +++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java @@ -5,7 +5,6 @@ import mod.azure.azurelib.rewrite.animation.cache.AzIdentifiableItemStackAnimatorCache; import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; -import net.minecraft.core.UUIDUtil; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; @@ -13,15 +12,6 @@ public class AzItemStackDispatchCommandPacket extends AbstractPacket { - // TODO: Updated encode/receive methods for AzCommand.CODEC/dispatchCommand - public static final StreamCodec CODEC = StreamCodec.composite( - UUIDUtil.STREAM_CODEC, - AzItemStackDispatchCommandPacket::itemStackId, - AzCommand.CODEC, - AzItemStackDispatchCommandPacket::dispatchCommand, - AzItemStackDispatchCommandPacket::new - ); - private final UUID itemStackId; private final AzCommand dispatchCommand; @@ -35,9 +25,8 @@ public AzItemStackDispatchCommandPacket( @Override public void encode(FriendlyByteBuf buf) { - buf.writeUUID(this.itemStackId); - // TODO: Needs fixed - // AzCommand.CODEC + buf.writeUUID(this.itemStackId); // Encode the UUID + AzCommand.ENCODER.accept(buf, this.dispatchCommand); // Encode AzCommand } @Override @@ -46,11 +35,9 @@ public ResourceLocation getPacketID() { } public static AzItemStackDispatchCommandPacket receive(FriendlyByteBuf buf) { - var readUUID = buf.readUUID(); - // TODO: Needs fixed - // AzCommand azCommand = buf.readUtf(); - - return new AzItemStackDispatchCommandPacket(readUUID, azCommand); + UUID itemStackId = buf.readUUID(); // Decode UUID + AzCommand dispatchCommand = AzCommand.DECODER.apply(buf); // Decode AzCommand + return new AzItemStackDispatchCommandPacket(itemStackId, dispatchCommand); // Create and return the packet instance } public void handle() { diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeExecutor.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeExecutor.java index 4fea0b0ef..f597dc7ca 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeExecutor.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeExecutor.java @@ -5,6 +5,7 @@ import mod.azure.azurelib.core.molang.MolangQueries; import mod.azure.azurelib.core.object.Axis; import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; +import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerTimer; import mod.azure.azurelib.rewrite.animation.controller.AzBoneAnimationQueueCache; import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation; import org.jetbrains.annotations.NotNull; @@ -41,16 +42,16 @@ public AzKeyframeExecutor( * bone, or continue with the remaining bones */ public void execute(@NotNull AzQueuedAnimation currentAnimation, T animatable, boolean crashWhenCantFindBone) { - var keyframeCallbackHandler = animationController.keyframeManager().keyframeCallbackHandler(); - var controllerTimer = animationController.controllerTimer(); - var transitionLength = animationController.animationProperties().transitionLength(); + AzKeyframeCallbackHandler keyframeCallbackHandler = animationController.keyframeManager().keyframeCallbackHandler(); + AzAnimationControllerTimer controllerTimer = animationController.controllerTimer(); + float transitionLength = animationController.animationProperties().transitionLength(); final double finalAdjustedTick = controllerTimer.getAdjustedTick(); MolangParser.INSTANCE.setMemoizedValue(MolangQueries.ANIM_TIME, () -> finalAdjustedTick / 20d); - for (var boneAnimation : currentAnimation.animation().boneAnimations()) { - var boneAnimationQueue = boneAnimationQueueCache.getOrNull(boneAnimation.boneName()); + for (AzBoneAnimation boneAnimation : currentAnimation.animation().boneAnimations()) { + AzBoneAnimationQueue boneAnimationQueue = boneAnimationQueueCache.getOrNull(boneAnimation.boneName()); if (boneAnimationQueue == null) { if (crashWhenCantFindBone) { @@ -60,10 +61,10 @@ public void execute(@NotNull AzQueuedAnimation currentAnimation, T animatable, b continue; } - var rotationKeyframes = boneAnimation.rotationKeyframes(); - var positionKeyframes = boneAnimation.positionKeyframes(); - var scaleKeyframes = boneAnimation.scaleKeyframes(); - var adjustedTick = controllerTimer.getAdjustedTick(); + AzKeyframeStack> rotationKeyframes = boneAnimation.rotationKeyframes(); + AzKeyframeStack> positionKeyframes = boneAnimation.positionKeyframes(); + AzKeyframeStack> scaleKeyframes = boneAnimation.scaleKeyframes(); + double adjustedTick = controllerTimer.getAdjustedTick(); updateRotation(rotationKeyframes, boneAnimationQueue, adjustedTick); updatePosition(positionKeyframes, boneAnimationQueue, adjustedTick); @@ -85,9 +86,9 @@ private void updateRotation( return; } - var x = getAnimationPointAtTick(keyframes.xKeyframes(), adjustedTick, true, Axis.X); - var y = getAnimationPointAtTick(keyframes.yKeyframes(), adjustedTick, true, Axis.Y); - var z = getAnimationPointAtTick(keyframes.zKeyframes(), adjustedTick, true, Axis.Z); + AzAnimationPoint x = getAnimationPointAtTick(keyframes.xKeyframes(), adjustedTick, true, Axis.X); + AzAnimationPoint y = getAnimationPointAtTick(keyframes.yKeyframes(), adjustedTick, true, Axis.Y); + AzAnimationPoint z = getAnimationPointAtTick(keyframes.zKeyframes(), adjustedTick, true, Axis.Z); queue.addRotations(x, y, z); } @@ -101,9 +102,9 @@ private void updatePosition( return; } - var x = getAnimationPointAtTick(keyframes.xKeyframes(), adjustedTick, false, Axis.X); - var y = getAnimationPointAtTick(keyframes.yKeyframes(), adjustedTick, false, Axis.Y); - var z = getAnimationPointAtTick(keyframes.zKeyframes(), adjustedTick, false, Axis.Z); + AzAnimationPoint x = getAnimationPointAtTick(keyframes.xKeyframes(), adjustedTick, false, Axis.X); + AzAnimationPoint y = getAnimationPointAtTick(keyframes.yKeyframes(), adjustedTick, false, Axis.Y); + AzAnimationPoint z = getAnimationPointAtTick(keyframes.zKeyframes(), adjustedTick, false, Axis.Z); queue.addPositions(x, y, z); } @@ -117,9 +118,9 @@ private void updateScale( return; } - var x = getAnimationPointAtTick(keyframes.xKeyframes(), adjustedTick, false, Axis.X); - var y = getAnimationPointAtTick(keyframes.yKeyframes(), adjustedTick, false, Axis.Y); - var z = getAnimationPointAtTick(keyframes.zKeyframes(), adjustedTick, false, Axis.Z); + AzAnimationPoint x = getAnimationPointAtTick(keyframes.xKeyframes(), adjustedTick, false, Axis.X); + AzAnimationPoint y = getAnimationPointAtTick(keyframes.yKeyframes(), adjustedTick, false, Axis.Y); + AzAnimationPoint z = getAnimationPointAtTick(keyframes.zKeyframes(), adjustedTick, false, Axis.Z); queue.addScales(x, y, z); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java index d2da7d054..5b7e94900 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java @@ -6,6 +6,8 @@ import org.jetbrains.annotations.NotNull; import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; /** * This enum represents the dispatch side for animation commands, which can either be client-side or server-side. It is @@ -34,10 +36,14 @@ public enum AzDispatchSide implements StringRepresentable { this.id = id; } - public static final StreamCodec CODEC = StreamCodec.of( - (buf, val) -> buf.writeByte(val.id), - buf -> ID_TO_ENUM_MAP.get((int) buf.readByte()) - ); + public static final Function DECODER = buf -> { + int id = buf.readByte(); // Read byte and convert to int + return ID_TO_ENUM_MAP.get(id); // Get enum from ID + }; + + public static final BiConsumer ENCODER = (buf, val) -> { + buf.writeByte(val.id); // Write the integer ID to the buffer + }; @Override public @NotNull String getSerializedName() { diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java index 9cb377df4..e228c330e 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; /** * Represents a command structure used to dispatch a sequence of actions in the animation system. This class primarily @@ -31,11 +33,19 @@ */ public record AzCommand(List actions) { - public static final StreamCodec CODEC = StreamCodec.composite( - new AzListStreamCodec<>(AzAction.CODEC), - AzCommand::actions, - AzCommand::new - ); + public static final AzListStreamCodec ACTION_LIST_CODEC = + new AzListStreamCodec<>(AzAction::decode, (buf, action) -> action.encode(buf)); + + public static final Function DECODER = buf -> { + // Decode the list of actions using the AzListStreamCodec + List actions = ACTION_LIST_CODEC.decode(buf); + return new AzCommand(actions); + }; + + public static final BiConsumer ENCODER = (buf, command) -> { + // Encode the list of actions using the AzListStreamCodec + ACTION_LIST_CODEC.encode(buf, command.actions()); + }; public static AzRootCommandBuilder builder() { return new AzRootCommandBuilder(); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java index c0de1bf4a..c58f211cc 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java @@ -3,6 +3,7 @@ import mod.azure.azurelib.rewrite.animation.AzAnimator; import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide; import mod.azure.azurelib.rewrite.animation.dispatch.command.action.codec.AzActionCodec; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; /** @@ -13,7 +14,21 @@ */ public interface AzAction { - AzActionCodec CODEC = new AzActionCodec(); + /** + * Decodes an AzAction from a {@link FriendlyByteBuf}. + * Delegates to {@link AzActionCodec#decode(FriendlyByteBuf)} for decoding logic. + */ + static AzAction decode(FriendlyByteBuf byteBuf) { + return new AzActionCodec().decode(byteBuf); + } + + /** + * Encodes this AzAction into a {@link FriendlyByteBuf}. + * Delegates to {@link AzActionCodec#encode(FriendlyByteBuf, AzAction)} for encoding logic. + */ + default void encode(FriendlyByteBuf byteBuf) { + new AzActionCodec().encode(byteBuf, this); + } void handle(AzDispatchSide originSide, AzAnimator animator); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java index 8b7f3726d..f300f5fd0 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java @@ -15,46 +15,55 @@ * Use this implementation in scenarios where AzAction objects need to be serialized or deserialized for efficient data * transmission or storage. */ -public class AzActionCodec implements StreamCodec { +/** + * The AzActionCodec class is responsible for encoding and decoding {@link AzAction} instances in 1.20.1, + * using the {@link FriendlyByteBuf} and the {@link AzActionRegistry} for maintaining associations + * between resource locations and their respective action codecs. + */ +public class AzActionCodec { - @Override public @NotNull AzAction decode(@NotNull FriendlyByteBuf byteBuf) { + // Decode the ID for the corresponding AzAction var id = byteBuf.readShort(); - var codec = AzActionRegistry - .>getCodecOrNull(id); + // Retrieve the action's codec using its ID from the registry + var codec = AzActionRegistry.getDecoderOrNull(id); if (codec == null) { throw new NullPointerException( - "Could not find action codec for a given action id while decoding data. ID: " + id + "Could not find action decoder for a given action ID while decoding data. ID: " + id ); } - return codec.decode(byteBuf); + // Use the codec to decode the AzAction + return codec.apply(byteBuf); } - @Override public void encode(@NotNull FriendlyByteBuf byteBuf, @NotNull AzAction action) { + // Get the resource location for the AzAction var resourceLocation = action.getResourceLocation(); + // Retrieve the corresponding ID and codec for the resource location var id = AzActionRegistry.getIdOrNull(resourceLocation); - var codec = AzActionRegistry - .>getCodecOrNull(resourceLocation); + var encoder = AzActionRegistry.getEncoderOrNull(resourceLocation); if (id == null) { throw new NullPointerException( - "Could not find action id for a given resource location while encoding data. Resource Location: " - + resourceLocation + "Could not find action ID for a given resource location while encoding data. Resource Location: " + + resourceLocation ); } + // Write the ID to the buffer byteBuf.writeShort(id); - if (codec == null) { + if (encoder == null) { throw new NullPointerException( - "Could not find action codec for a given resource location while encoding data. Resource Location: " - + resourceLocation + ", ID: " + id + "Could not find action encoder for a given resource location while encoding data. Resource Location: " + + resourceLocation + ", ID: " + id ); } - codec.encode(byteBuf, action); + // Use the encoder to encode the AzAction + encoder.accept(byteBuf, action); } } + diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java index 1f00b588d..67d6266e5 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java @@ -7,6 +7,9 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; +import java.util.function.BiConsumer; +import java.util.function.Function; + /** * Represents an action that cancels the current animation of a specified animation controller in the animation system. * This action is part of the root-level dispatch actions and interacts with a specific animation controller by name. @@ -47,11 +50,14 @@ public record AzRootCancelAction( String controllerName ) implements AzAction { - public static final StreamCodec CODEC = StreamCodec.composite( - ByteBufCodecs.STRING_UTF8, - AzRootCancelAction::controllerName, - AzRootCancelAction::new - ); + public static final Function DECODER = buf -> { + String controllerName = buf.readUtf(); // Read UTF-8 string for the controller's name + return new AzRootCancelAction(controllerName); + }; + + public static final BiConsumer ENCODER = (buf, action) -> { + buf.writeUtf(action.controllerName()); // Write UTF-8 string for the controller's name + }; public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/cancel"); @@ -68,4 +74,12 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } + + public static AzRootCancelAction decode(FriendlyByteBuf buf) { + return DECODER.apply(buf); // Delegate to the DECODER functional interface + } + + public static void encode(FriendlyByteBuf buf, AzRootCancelAction action) { + ENCODER.accept(buf, action); // Delegate to the ENCODER functional interface + } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java index 3c0b55062..abdbf64ee 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java @@ -7,6 +7,9 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; +import java.util.function.BiConsumer; +import java.util.function.Function; + /** * The AzRootCancelAllAction class implements the AzAction interface and defines an action that cancels all ongoing * animations within an animator by setting the current animation of all controllers to null.
@@ -18,8 +21,11 @@ public class AzRootCancelAllAction implements AzAction { public static final AzRootCancelAllAction INSTANCE = new AzRootCancelAllAction(); - public static final StreamCodec CODEC = StreamCodec.unit(INSTANCE); + public static final Function DECODER = buf -> INSTANCE; + public static final BiConsumer ENCODER = (buf, action) -> { + // No data to write since this is a singleton + }; public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/cancel_all"); private AzRootCancelAllAction() {} @@ -36,4 +42,12 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } + + public static AzRootCancelAllAction decode(FriendlyByteBuf buf) { + return DECODER.apply(buf); // Always returns the singleton instance + } + + public static void encode(FriendlyByteBuf buf, AzRootCancelAllAction action) { + ENCODER.accept(buf, action); // Does nothing since no data is encoded + } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java index 77b828ec5..4c8658bef 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java @@ -8,18 +8,24 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; +import java.util.function.BiConsumer; +import java.util.function.Function; + public record AzRootPlayAnimationSequenceAction( String controllerName, AzAnimationSequence sequence ) implements AzAction { - public static final StreamCodec CODEC = StreamCodec.composite( - ByteBufCodecs.STRING_UTF8, - AzRootPlayAnimationSequenceAction::controllerName, - AzAnimationSequence.CODEC, - AzRootPlayAnimationSequenceAction::sequence, - AzRootPlayAnimationSequenceAction::new - ); + public static final Function DECODER = buf -> { + String controllerName = buf.readUtf(); // Read controller name (UTF string) + AzAnimationSequence sequence = AzAnimationSequence.DECODER.apply(buf); // Decode AzAnimationSequence + return new AzRootPlayAnimationSequenceAction(controllerName, sequence); // Create new instance + }; + + public static final BiConsumer ENCODER = (buf, action) -> { + buf.writeUtf(action.controllerName()); // Write controller name (UTF string) + AzAnimationSequence.ENCODER.accept(buf, action.sequence()); // Encode AzAnimationSequence + }; public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/play_animation_sequence"); @@ -36,4 +42,12 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } + + public static AzRootPlayAnimationSequenceAction decode(FriendlyByteBuf buf) { + return DECODER.apply(buf); // Delegate decoding to DECODER + } + + public static void encode(FriendlyByteBuf buf, AzRootPlayAnimationSequenceAction action) { + ENCODER.accept(buf, action); // Delegate encoding to ENCODER + } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java index 826e92fd5..f819940b1 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java @@ -7,15 +7,21 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; +import java.util.function.BiConsumer; +import java.util.function.Function; + public record AzRootSetAnimationSpeedAction( double animationSpeed ) implements AzAction { - public static final StreamCodec CODEC = StreamCodec.composite( - ByteBufCodecs.DOUBLE, - AzRootSetAnimationSpeedAction::animationSpeed, - AzRootSetAnimationSpeedAction::new - ); + public static final Function DECODER = buf -> { + double animationSpeed = buf.readDouble(); // Read double from the buffer + return new AzRootSetAnimationSpeedAction(animationSpeed); // Create a new instance + }; + + public static final BiConsumer ENCODER = (buf, action) -> { + buf.writeDouble(action.animationSpeed()); // Write the animation speed to the buffer + }; public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/set_animation_speed"); @@ -34,4 +40,12 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } + + public static AzRootSetAnimationSpeedAction decode(FriendlyByteBuf buf) { + return DECODER.apply(buf); // Delegate decoding to DECODER + } + + public static void encode(FriendlyByteBuf buf, AzRootSetAnimationSpeedAction action) { + ENCODER.accept(buf, action); // Delegate encoding to ENCODER + } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java index b6c61b8bd..e8d163136 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java @@ -8,15 +8,21 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; +import java.util.function.BiConsumer; +import java.util.function.Function; + public record AzRootSetEasingTypeAction( AzEasingType easingType ) implements AzAction { - public static final StreamCodec CODEC = StreamCodec.composite( - AzEasingType.STREAM_CODEC, - AzRootSetEasingTypeAction::easingType, - AzRootSetEasingTypeAction::new - ); + public static final Function DECODER = buf -> { + AzEasingType easingType = AzEasingType.DECODER.apply(buf); + return new AzRootSetEasingTypeAction(easingType); + }; + + public static final BiConsumer ENCODER = (buf, action) -> { + AzEasingType.ENCODER.accept(buf, action.easingType()); + }; public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/set_easing_type"); @@ -35,4 +41,13 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } + + public static AzRootSetEasingTypeAction decode(FriendlyByteBuf buf) { + return DECODER.apply(buf); + } + + public static void encode(FriendlyByteBuf buf, AzRootSetEasingTypeAction action) { + ENCODER.accept(buf, action); + } + } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java index 0d912523d..1ce7c1624 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java @@ -7,6 +7,9 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; +import java.util.function.BiConsumer; +import java.util.function.Function; + /** * The {@code AzRootSetTransitionSpeedAction} class implements the {@link AzAction} interface and represents an action * that modifies the transition speed for an animator during an animation state. This action is intended for use within @@ -18,11 +21,14 @@ public record AzRootSetTransitionSpeedAction( float transitionSpeed ) implements AzAction { - public static final StreamCodec CODEC = StreamCodec.composite( - ByteBufCodecs.FLOAT, - AzRootSetTransitionSpeedAction::transitionSpeed, - AzRootSetTransitionSpeedAction::new - ); + public static final Function DECODER = buf -> { + float transitionSpeed = buf.readFloat(); // Read float from the buffer + return new AzRootSetTransitionSpeedAction(transitionSpeed); // Create a new instance + }; + + public static final BiConsumer ENCODER = (buf, action) -> { + buf.writeFloat(action.transitionSpeed()); // Write the transition speed to the buffer + }; public static final ResourceLocation RESOURCE_LOCATION = AzureLib.modResource("root/set_transition_speed"); @@ -41,4 +47,12 @@ public void handle(AzDispatchSide originSide, AzAnimator animator) { public ResourceLocation getResourceLocation() { return RESOURCE_LOCATION; } + + public static AzRootSetTransitionSpeedAction decode(FriendlyByteBuf buf) { + return DECODER.apply(buf); // Delegate decoding to DECODER + } + + public static void encode(FriendlyByteBuf buf, AzRootSetTransitionSpeedAction action) { + ENCODER.accept(buf, action); // Delegate encoding to ENCODER + } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java index 13de68e4a..1c09cfa64 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java @@ -4,74 +4,118 @@ import mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction; import mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.*; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.Nullable; import java.util.HashMap; import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; /** * The AzActionRegistry class serves as a centralized registry for mapping {@link AzAction} implementations to their - * associated {@link ResourceLocation} identifiers and codecs. This registry enables efficient encoding, decoding, and - * dispatching of animation-related actions within the animation system.
+ * associated {@link ResourceLocation} identifiers and corresponding encoders/decoders. This registry enables efficient + * encoding, decoding, and dispatching of animation-related actions within the animation system.
* Key Responsibilities: *
    *
  • Maintain a bidirectional mapping between {@link ResourceLocation} identifiers and short integer IDs for efficient * serialization/deserialization.
  • - *
  • Register {@link AzAction} implementations and their corresponding {@link StreamCodec} instances.
  • - *
  • Provide methods for retrieving codecs and IDs based on resource locations or integer IDs. + *
  • Register {@link AzAction} implementations and their corresponding encoders and decoders.
  • + *
  • Provide methods for retrieving encoders/decoders and IDs based on resource locations or integer IDs. *
*/ public class AzActionRegistry { + // Mappings for resource location to ID and encoders/decoders by ID private static final Map RESOURCE_LOCATION_TO_ID = new Object2ShortArrayMap<>(); - private static final Map> CODEC_BY_ID = - new HashMap<>(); + private static final Map> DECODERS_BY_ID = new HashMap<>(); + + private static final Map> ENCODERS_BY_ID = new HashMap<>(); private static short NEXT_FREE_ID = 0; static { - // Root actions - register(AzRootCancelAction.RESOURCE_LOCATION, AzRootCancelAction.CODEC); - register(AzRootCancelAllAction.RESOURCE_LOCATION, AzRootCancelAllAction.CODEC); - register(AzRootPlayAnimationSequenceAction.RESOURCE_LOCATION, AzRootPlayAnimationSequenceAction.CODEC); - register(AzRootSetAnimationSpeedAction.RESOURCE_LOCATION, AzRootSetAnimationSpeedAction.CODEC); - register(AzRootSetEasingTypeAction.RESOURCE_LOCATION, AzRootSetEasingTypeAction.CODEC); - register(AzRootSetTransitionSpeedAction.RESOURCE_LOCATION, AzRootSetTransitionSpeedAction.CODEC); + // Register root actions + register( + AzRootCancelAction.RESOURCE_LOCATION, + AzRootCancelAction::decode, // Decoder function + AzRootCancelAction::encode // Encoder function + ); + register( + AzRootCancelAllAction.RESOURCE_LOCATION, + AzRootCancelAllAction::decode, + AzRootCancelAllAction::encode + ); + register( + AzRootPlayAnimationSequenceAction.RESOURCE_LOCATION, + AzRootPlayAnimationSequenceAction::decode, + AzRootPlayAnimationSequenceAction::encode + ); + register( + AzRootSetAnimationSpeedAction.RESOURCE_LOCATION, + AzRootSetAnimationSpeedAction::decode, + AzRootSetAnimationSpeedAction::encode + ); + register( + AzRootSetEasingTypeAction.RESOURCE_LOCATION, + AzRootSetEasingTypeAction::decode, + AzRootSetEasingTypeAction::encode + ); + register( + AzRootSetTransitionSpeedAction.RESOURCE_LOCATION, + AzRootSetTransitionSpeedAction::decode, + AzRootSetTransitionSpeedAction::encode + ); + } - // Controller actions - // TODO: + /** + * Returns a decoder function for the given {@link ResourceLocation}. + */ + public static @Nullable Function getDecoderOrNull(ResourceLocation resourceLocation) { + var id = RESOURCE_LOCATION_TO_ID.get(resourceLocation); + return DECODERS_BY_ID.get(id); + } - // Animation actions - // TODO: + /** + * Returns a decoder function for the given ID. + */ + public static @Nullable Function getDecoderOrNull(short id) { + return DECODERS_BY_ID.get(id); } - public static @Nullable > T getCodecOrNull( - ResourceLocation resourceLocation - ) { + /** + * Returns an encoder function for the given {@link ResourceLocation}. + */ + public static @Nullable BiConsumer getEncoderOrNull(ResourceLocation resourceLocation) { var id = RESOURCE_LOCATION_TO_ID.get(resourceLocation); - @SuppressWarnings("unchecked") - var codec = (T) CODEC_BY_ID.get(id); - return codec; + return ENCODERS_BY_ID.get(id); } - public static @Nullable > T getCodecOrNull(short id) { - @SuppressWarnings("unchecked") - var codec = (T) CODEC_BY_ID.get(id); - return codec; + /** + * Returns an encoder function for the given ID. + */ + public static @Nullable BiConsumer getEncoderOrNull(short id) { + return ENCODERS_BY_ID.get(id); } + /** + * Returns the ID associated with a given {@link ResourceLocation}. + */ public static @Nullable Short getIdOrNull(ResourceLocation resourceLocation) { return RESOURCE_LOCATION_TO_ID.get(resourceLocation); } + /** + * Registers a new action with its resource location, decoder, and encoder. + */ private static void register( - ResourceLocation resourceLocation, - StreamCodec codec + ResourceLocation resourceLocation, + Function decoder, + BiConsumer encoder ) { var id = RESOURCE_LOCATION_TO_ID.computeIfAbsent(resourceLocation, ($) -> NEXT_FREE_ID++); - CODEC_BY_ID.put(id, codec); + DECODERS_BY_ID.put(id, (Function) decoder); + ENCODERS_BY_ID.put(id, (BiConsumer) encoder); } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java index fb117fc24..18ee2a4af 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java @@ -5,14 +5,23 @@ import net.minecraft.network.FriendlyByteBuf; import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; public record AzAnimationSequence( List stages ) { - public static final StreamCodec CODEC = StreamCodec.composite( - new AzListStreamCodec<>(AzAnimationStage.CODEC), - AzAnimationSequence::stages, - AzAnimationSequence::new - ); + private static final AzListStreamCodec STAGE_LIST_CODEC = + new AzListStreamCodec<>(AzAnimationStage.DECODER, AzAnimationStage.ENCODER); + + public static final Function DECODER = buf -> { + List stages = STAGE_LIST_CODEC.decode(buf); + return new AzAnimationSequence(stages); + }; + + public static final BiConsumer ENCODER = (buf, sequence) -> { + STAGE_LIST_CODEC.encode(buf, sequence.stages()); + }; + } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java index a3edbe88e..84886e91d 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java @@ -3,17 +3,24 @@ import mod.azure.azurelib.rewrite.animation.property.AzAnimationStageProperties; import net.minecraft.network.FriendlyByteBuf; +import java.util.function.BiConsumer; +import java.util.function.Function; + public record AzAnimationStage( String name, AzAnimationStageProperties properties ) { - public static final StreamCodec CODEC = StreamCodec.composite( - ByteBufCodecs.STRING_UTF8, - AzAnimationStage::name, - AzAnimationStageProperties.CODEC, - AzAnimationStage::properties, - AzAnimationStage::new - ); + public static final Function DECODER = buf -> { + String name = buf.readUtf(); + AzAnimationStageProperties properties = AzAnimationStageProperties.DECODER.apply(buf); + return new AzAnimationStage(name, properties); + }; + + public static final BiConsumer ENCODER = (buf, stage) -> { + buf.writeUtf(stage.name()); + AzAnimationStageProperties.ENCODER.accept(buf, stage.properties()); + }; + } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java index fa7c3656b..50176a2b8 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java @@ -6,6 +6,8 @@ import net.minecraft.network.FriendlyByteBuf; import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Function; public interface AzEasingType { @@ -13,10 +15,10 @@ public interface AzEasingType { Double2DoubleFunction buildTransformer(Double value); - StreamCodec STREAM_CODEC = StreamCodec.of( - (buf, val) -> buf.writeUtf(val.name()), - buf -> Objects.requireNonNull(AzEasingTypeRegistry.getOrNull(buf.readUtf())) - ); + Function DECODER = buf -> + Objects.requireNonNull(AzEasingTypeRegistry.getOrNull(buf.readUtf())); + + BiConsumer ENCODER = (buf, val) -> buf.writeUtf(val.name()); default double apply(AzAnimationPoint animationPoint) { Double easingVariable = null; diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationProperties.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationProperties.java index f1b6f9a18..c54b6aa70 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationProperties.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationProperties.java @@ -3,14 +3,19 @@ import mod.azure.azurelib.rewrite.animation.easing.AzEasingType; import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypes; import mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationPropertiesCodec; +import net.minecraft.network.FriendlyByteBuf; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Function; public class AzAnimationProperties { - public static final AzAnimationPropertiesCodec CODEC = new AzAnimationPropertiesCodec(); + public static final Function DECODER = AzAnimationPropertiesCodec.DECODER; + + public static final BiConsumer ENCODER = AzAnimationPropertiesCodec.ENCODER; public static final AzAnimationProperties DEFAULT = new AzAnimationProperties(1D, AzEasingTypes.NONE, 0F); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationStageProperties.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationStageProperties.java index 1432486f8..e126364db 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationStageProperties.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationStageProperties.java @@ -5,14 +5,19 @@ import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehavior; import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviors; import mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationStagePropertiesCodec; +import net.minecraft.network.FriendlyByteBuf; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Function; public class AzAnimationStageProperties extends AzAnimationProperties { - public static final AzAnimationStagePropertiesCodec CODEC = new AzAnimationStagePropertiesCodec(); + public static final Function DECODER = AzAnimationStagePropertiesCodec.DECODER; + + public static final BiConsumer ENCODER = AzAnimationStagePropertiesCodec.ENCODER; public static final AzAnimationStageProperties DEFAULT = new AzAnimationStageProperties( 1D, diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java index aa6877b8e..4890b71ee 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java @@ -4,12 +4,13 @@ import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypes; import mod.azure.azurelib.rewrite.animation.property.AzAnimationProperties; import net.minecraft.network.FriendlyByteBuf; -import org.jetbrains.annotations.NotNull; +import java.util.function.BiConsumer; +import java.util.function.Function; -public class AzAnimationPropertiesCodec implements StreamCodec { - @Override - public @NotNull AzAnimationProperties decode(FriendlyByteBuf buf) { +public class AzAnimationPropertiesCodec { + + public static final Function DECODER = buf -> { var propertyLength = buf.readByte(); var properties = AzAnimationProperties.EMPTY; @@ -27,10 +28,9 @@ public class AzAnimationPropertiesCodec implements StreamCodec ENCODER = (buf, properties) -> { var propertyLength = 0; propertyLength += properties.hasAnimationSpeed() ? 1 : 0; propertyLength += properties.hasTransitionLength() ? 1 : 0; @@ -52,5 +52,5 @@ public void encode(FriendlyByteBuf buf, AzAnimationProperties properties) { buf.writeByte(2); buf.writeUtf(properties.easingType().name()); } - } + }; } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java index 1a4b76867..9b8dccf6e 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java @@ -8,10 +8,12 @@ import net.minecraft.network.FriendlyByteBuf; import org.jetbrains.annotations.NotNull; -public class AzAnimationStagePropertiesCodec implements StreamCodec { +import java.util.function.BiConsumer; +import java.util.function.Function; - @Override - public @NotNull AzAnimationStageProperties decode(FriendlyByteBuf buf) { +public class AzAnimationStagePropertiesCodec { + + public static final Function DECODER = buf -> { var propertyLength = buf.readByte(); var properties = AzAnimationStageProperties.EMPTY; @@ -33,10 +35,9 @@ public class AzAnimationStagePropertiesCodec implements StreamCodec ENCODER = (buf, properties) -> { var propertyLength = 0; propertyLength += properties.hasAnimationSpeed() ? 1 : 0; propertyLength += properties.hasTransitionLength() ? 1 : 0; @@ -64,5 +65,5 @@ public void encode(FriendlyByteBuf buf, AzAnimationStageProperties properties) { buf.writeByte(3); buf.writeUtf(properties.playBehavior().name()); } - } + }; } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/util/codec/AzListStreamCodec.java b/common/src/main/java/mod/azure/azurelib/rewrite/util/codec/AzListStreamCodec.java index e5d25c888..b92234216 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/util/codec/AzListStreamCodec.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/util/codec/AzListStreamCodec.java @@ -5,30 +5,33 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; -public class AzListStreamCodec implements StreamCodec> { +public class AzListStreamCodec { - private final StreamCodec codec; + private final Function decoder; // Function to decode an element from the buffer + private final BiConsumer encoder; // BiConsumer to encode an element into the buffer - public AzListStreamCodec(StreamCodec codec) { - this.codec = codec; + public AzListStreamCodec(Function decoder, BiConsumer encoder) { + this.decoder = decoder; + this.encoder = encoder; } - @Override public @NotNull List decode(FriendlyByteBuf buf) { - var size = buf.readByte(); - var list = new ArrayList(size); + int size = buf.readByte(); // Read the size of the list + List list = new ArrayList<>(size); for (int i = 0; i < size; i++) { - list.add(codec.decode(buf)); + list.add(decoder.apply(buf)); // Decode each element using the provided decoder } return list; } - @Override public void encode(FriendlyByteBuf buf, List elements) { - buf.writeByte(elements.size()); - elements.forEach(element -> codec.encode(buf, element)); + buf.writeByte(elements.size()); // Write the size of the list + elements.forEach(element -> encoder.accept(buf, element)); // Encode each element using the provided encoder } + } From 879d78bf0bd9155a4ac90828b757ef3b542caf1b Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 16:26:26 -0400 Subject: [PATCH 15/40] Use a base variable for NTB tag name. --- common/src/main/java/mod/azure/azurelib/AzureLib.java | 1 + .../azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java | 3 ++- .../animation/cache/AzIdentifiableItemStackAnimatorCache.java | 3 ++- .../azurelib/rewrite/animation/dispatch/command/AzCommand.java | 2 +- fabric/src/main/resources/azurelib.fabric.mixins.json | 2 +- neoforge/src/main/resources/azurelib.forge.mixins.json | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/AzureLib.java b/common/src/main/java/mod/azure/azurelib/AzureLib.java index 51d207d90..03b100258 100644 --- a/common/src/main/java/mod/azure/azurelib/AzureLib.java +++ b/common/src/main/java/mod/azure/azurelib/AzureLib.java @@ -18,6 +18,7 @@ public class AzureLib { public static final Logger LOGGER = LogManager.getLogger("azurelib"); public static final Marker MAIN_MARKER = MarkerManager.getMarker("main"); public static final String MOD_ID = "azurelib"; + public static final String ITEM_UUID_TAG = "az_id"; public static boolean hasInitialized; public static void initialize() { diff --git a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java index e61f5e3c3..0d6d12e3d 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java @@ -1,5 +1,6 @@ package mod.azure.azurelib.mixins; +import mod.azure.azurelib.AzureLib; import mod.azure.azurelib.rewrite.animation.AzAnimator; import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor; import mod.azure.azurelib.rewrite.animation.cache.AzIdentifiableItemStackAnimatorCache; @@ -21,7 +22,7 @@ public void setAnimator(@Nullable AzAnimator animator) { @Override public @Nullable AzAnimator getAnimatorOrNull() { var self = AzureLibUtil.self(this); - var uuid = self.getOrCreateTag().getUUID("az_id"); + var uuid = self.getOrCreateTag().getUUID(AzureLib.ITEM_UUID_TAG); return AzIdentifiableItemStackAnimatorCache.getInstance().getOrNull(uuid); } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java index 220c63bfc..3a16d6794 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java @@ -1,5 +1,6 @@ package mod.azure.azurelib.rewrite.animation.cache; +import mod.azure.azurelib.AzureLib; import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; @@ -33,7 +34,7 @@ public void add(ItemStack itemStack, AzItemAnimator animator) { } var tag = itemStack.getTag(); - var uuid = tag.getUUID("az_id"); + var uuid = tag.getUUID(AzureLib.ITEM_UUID_TAG); if (uuid != null) { ANIMATORS_BY_UUID.computeIfAbsent(uuid, ($) -> animator); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java index e228c330e..f20620172 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java @@ -142,7 +142,7 @@ public void sendForItem(Entity entity, ItemStack itemStack) { if (entity.level().isClientSide()) { dispatchFromClient(entity); } else { - var uuid = itemStack.getTag().getUUID("az_id"); + var uuid = itemStack.getTag().getUUID(AzureLib.ITEM_UUID_TAG); if (uuid == null) { AzureLib.LOGGER.warn( diff --git a/fabric/src/main/resources/azurelib.fabric.mixins.json b/fabric/src/main/resources/azurelib.fabric.mixins.json index 427a6bc2c..59e91a49c 100644 --- a/fabric/src/main/resources/azurelib.fabric.mixins.json +++ b/fabric/src/main/resources/azurelib.fabric.mixins.json @@ -6,6 +6,7 @@ "defaultRequire": 1 }, "mixins": [ + "ItemStackMixin_AzItemStackIdentityRegistry", "PlayerListMixin" ], "client": [ @@ -13,7 +14,6 @@ "BlockEntityMixin_AzBlockEntityAnimatorCache", "EntityMixin_AzEntityAnimatorCache", "ItemStackMixin_AzItemAnimatorCache", - "ItemStackMixin_AzItemStackIdentityRegistry", "FabricMixinHumanoidArmorLayer", "ItemRendererAccessor", "MinecraftMixin", diff --git a/neoforge/src/main/resources/azurelib.forge.mixins.json b/neoforge/src/main/resources/azurelib.forge.mixins.json index 9dc524092..f9618f9cf 100644 --- a/neoforge/src/main/resources/azurelib.forge.mixins.json +++ b/neoforge/src/main/resources/azurelib.forge.mixins.json @@ -7,6 +7,7 @@ "defaultRequire": 1 }, "mixins": [ + "ItemStackMixin_AzItemStackIdentityRegistry", "PlayerListMixin" ], "client": [ @@ -14,7 +15,6 @@ "BlockEntityMixin_AzBlockEntityAnimatorCache", "EntityMixin_AzEntityAnimatorCache", "ItemStackMixin_AzItemAnimatorCache", - "ItemStackMixin_AzItemStackIdentityRegistry", "ClientHooksMixin", "ItemRendererAccessor", "MinecraftMixin", From 7cdc7bee7ee2126bb939f0dcda5a831219fbdbc6 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 16:26:59 -0400 Subject: [PATCH 16/40] Clean up ItemStackMixin_AzItemStackIdentityRegistry to remove unneeded Inject calls --- ...tackMixin_AzItemStackIdentityRegistry.java | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java index be4bd8fba..ace602c39 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java @@ -1,15 +1,18 @@ package mod.azure.azurelib.mixins; +import mod.azure.azurelib.AzureLib; import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; import mod.azure.azurelib.util.AzureLibUtil; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; import org.spongepowered.asm.mixin.Mixin; 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.CallbackInfo; +import java.util.Optional; import java.util.UUID; /** @@ -26,34 +29,52 @@ @Mixin(ItemStack.class) public class ItemStackMixin_AzItemStackIdentityRegistry { + /** + * Injects into the constructor of the {@link ItemStack} that takes a {@link CompoundTag} parameter to initialize a unique AzureLib ID (Az ID) + * if the item is registered in the {@link AzIdentityRegistry}. + * + * @param compoundTag The {@link CompoundTag} associated with the {@link ItemStack}. + * @param ci The {@link CallbackInfo} for the mixin injection. + */ + @Inject( method = "(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("TAIL") ) - public void az_addIdentityComponent(CompoundTag compoundTag, CallbackInfo ci) { + public void azurelib$initializeAzIdFromCompoundTag(CompoundTag compoundTag, CallbackInfo ci) { azureLib$initializeAzIdOnStack(this, compoundTag); } - @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;)V", at = @At("TAIL")) - public void az_addIdentityComponentItemConstructor2(CallbackInfo ci) { - azureLib$initializeAzIdOnStack(this, null); - } - - @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/core/Holder;)V", at = @At("TAIL")) - public void az_addIdentityComponentItemConstructor3(CallbackInfo ci) { - azureLib$initializeAzIdOnStack(this, null); - } - + /** + * Injects into the constructor of the {@link ItemStack} that takes an {@link ItemLike}, an integer item count, and an {@link Optional} for + * the compound tag. This ensures that a unique AzureLib ID (Az ID) is initialized if the item is registered in {@link AzIdentityRegistry}. + * + * @param ci The {@link CallbackInfo} for the mixin injection. + */ @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;ILjava/util/Optional;)V", at = @At("TAIL")) - public void az_addIdentityComponentItemConstructor4(CallbackInfo ci) { + public void azurelib$initializeAzIdForConstructorWithOptional(CallbackInfo ci) { azureLib$initializeAzIdOnStack(this, null); } + /** + * Injects into the constructor of the {@link ItemStack} that takes an {@link ItemLike} and an integer count. + * This ensures that a unique AzureLib ID (Az ID) is initialized if the item is registered in {@link AzIdentityRegistry}. + * + * @param ci The {@link CallbackInfo} for the mixin injection. + */ @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;I)V", at = @At("TAIL")) - public void az_addIdentityComponentItemConstructor5(CallbackInfo ci) { + public void azurelib$initializeAzIdForConstructor(CallbackInfo ci) { azureLib$initializeAzIdOnStack(this, null); } + /** + * Ensures that a unique AzureLib ID (Az ID) is initialized on the provided stack object if the item it represents + * is registered in the {@link AzIdentityRegistry} and does not already have a unique identifier. If necessary, + * assigns a new {@link CompoundTag} for the stack and generates a new UUID. + * + * @param stackObject The object representing the stack, expected to be an instance of {@link ItemStack}. + * @param tag The {@link CompoundTag} associated with the stack, used for storing or retrieving data. + */ @Unique private void azureLib$initializeAzIdOnStack(Object stackObject, CompoundTag tag) { var self = AzureLibUtil.self(stackObject); @@ -64,8 +85,9 @@ public void az_addIdentityComponentItemConstructor5(CallbackInfo ci) { var stackTag = self.getTag(); - if (stackTag != null && AzIdentityRegistry.hasIdentity(self.getItem()) && !stackTag.hasUUID("az_id")) { - stackTag.putUUID("az_id", UUID.randomUUID()); + if (stackTag != null && AzIdentityRegistry.hasIdentity(self.getItem()) && !stackTag.hasUUID( + AzureLib.ITEM_UUID_TAG)) { + stackTag.putUUID(AzureLib.ITEM_UUID_TAG, UUID.randomUUID()); } } From 070fa743a0d1b9b5652e6f57c9986863163f9fa5 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 16:27:30 -0400 Subject: [PATCH 17/40] Fixed AzID being copied to a new stack on menu creation, now ensures each item stack has it's own Az_ID. Fixed AzID being copied to a new stack on menu creation, now ensures each item stack has it's own Az_ID. --- ...bstractContainerMenuMixin_AzItemIDFix.java | 114 ++++++++++++++++++ .../resources/azurelib.fabric.mixins.json | 1 + .../main/resources/azurelib.forge.mixins.json | 1 + 3 files changed, 116 insertions(+) create mode 100644 common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java diff --git a/common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java b/common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java new file mode 100644 index 000000000..350c99aea --- /dev/null +++ b/common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java @@ -0,0 +1,114 @@ +package mod.azure.azurelib.mixins; + +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.UUID; + +/** + * A Mixin extension for the {@code AbstractContainerMenu} class that introduces support for AzureLib-specific {@code ItemStack} + * identity management (Az ID). This Mixin ensures the proper handling, synchronization, and comparison of + * AzureLib-registered item stacks with custom identifiers during container interactions. + */ +@Mixin(AbstractContainerMenu.class) +public class AbstractContainerMenuMixin_AzItemIDFix { + + @Unique + private static final int DEFAULT_AZ_ID = -1; + + /** + * Removes the AzureLib-specific ID (Az ID) from a copied `ItemStack` during a container click action. This is only + * performed if the original stack's item is registered with AzureLib's identity registry. + *

+ * Tooltip: Prevents the propagation of the Az ID when `ItemStack` objects are copied during container interactions, + * keeping custom IDs only for registered items. + */ + @Redirect( + method = "doClick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/item/ItemStack;copyWithCount(I)Lnet/minecraft/world/item/ItemStack;", + ordinal = 1 + ) + ) + public ItemStack azurelib$syncAzureIDWithRemote(ItemStack itemStack, int count) { + var copyStack = itemStack.copyWithCount(count); + + if (AzIdentityRegistry.hasIdentity(itemStack.getItem()) && copyStack.hasTag() && copyStack.getTag().contains( + AzureLib.ITEM_UUID_TAG)) { + copyStack.getTag().putUUID(AzureLib.ITEM_UUID_TAG, UUID.randomUUID()); + } + + return copyStack; + } + + /** + * Validates AzureLib-specific IDs (Az ID) between two `ItemStack` objects during remote slot synchronization. This + * ensures that items with custom IDs remain synchronized correctly during slot operations. + *

+ * Tooltip: Compares two `ItemStack` objects, ensuring their Az IDs (if present) also match. + */ + @Redirect( + method = "synchronizeSlotToRemote", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/item/ItemStack;matches(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z" + ) + ) + public boolean azurelib$syncAzureIDWithRemote( + ItemStack itemStack, + ItemStack comparisonItemStack + ) { + return azurelib$compareStacksWithAzureID(itemStack, comparisonItemStack); + } + + /** + * Forces an Az ID comparison when listening for slot changes involving two `ItemStack` objects. + *

+ * Tooltip: Ensures that slot listeners detect changes in AzureLib-registered item stacks based not only on their + * normal properties but also their Az ID values, if applicable. + */ + @Redirect( + method = "triggerSlotListeners", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/item/ItemStack;matches(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z" + ) + ) + public boolean azurelib$detectSlotChangeWithAzureID( + ItemStack itemStack, + ItemStack comparisonItemStack + ) { + return azurelib$compareStacksWithAzureID(itemStack, comparisonItemStack); + } + + /** + * Compares two ItemStacks while considering AzureLib-specific IDs (Az ID) if the item is registered with AzureLib. + * + * @param itemStack The first ItemStack to compare. + * @param comparisonItemStack The second ItemStack to compare. + * @return True if the base comparison is true and the Az IDs (if present) match; false otherwise. + */ + @Unique + private boolean azurelib$compareStacksWithAzureID(ItemStack itemStack, ItemStack comparisonItemStack) { + return ItemStack.matches(itemStack, comparisonItemStack) && + (!AzIdentityRegistry.hasIdentity(itemStack.getItem()) || azurelib$checkAzIDMatch(itemStack.getTag(), comparisonItemStack.getTag())); + } + + /** + * Performs an exclusive-NOR (XNOR) operation on the Az ID tags of two ItemStacks to check for matching IDs. + * + * @param tag1 The CompoundTag of the first ItemStack. + * @param tag2 The CompoundTag of the second ItemStack. + * @return True if both tags either have matching Az IDs or are null, false otherwise. + */ + @Unique + private static boolean azurelib$checkAzIDMatch(CompoundTag tag1, CompoundTag tag2) { + return (tag1 == null ? DEFAULT_AZ_ID : tag1.getInt(AzureLib.ITEM_UUID_TAG)) == (tag2 == null ? DEFAULT_AZ_ID : tag2.getInt(AzureLib.ITEM_UUID_TAG)); + } + +} diff --git a/fabric/src/main/resources/azurelib.fabric.mixins.json b/fabric/src/main/resources/azurelib.fabric.mixins.json index 59e91a49c..313c28917 100644 --- a/fabric/src/main/resources/azurelib.fabric.mixins.json +++ b/fabric/src/main/resources/azurelib.fabric.mixins.json @@ -6,6 +6,7 @@ "defaultRequire": 1 }, "mixins": [ + "AbstractContainerMenuMixin_AzItemIDFix", "ItemStackMixin_AzItemStackIdentityRegistry", "PlayerListMixin" ], diff --git a/neoforge/src/main/resources/azurelib.forge.mixins.json b/neoforge/src/main/resources/azurelib.forge.mixins.json index f9618f9cf..33b98a51e 100644 --- a/neoforge/src/main/resources/azurelib.forge.mixins.json +++ b/neoforge/src/main/resources/azurelib.forge.mixins.json @@ -7,6 +7,7 @@ "defaultRequire": 1 }, "mixins": [ + "AbstractContainerMenuMixin_AzItemIDFix", "ItemStackMixin_AzItemStackIdentityRegistry", "PlayerListMixin" ], From 770883fe33040b6c36bbf4f82808de7be59b9552 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 16:27:35 -0400 Subject: [PATCH 18/40] Update PistolItem.java --- .../main/java/mod/azure/azurelib/testing/item/PistolItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java b/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java index e2687e6d4..e5c17dd50 100644 --- a/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java +++ b/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java @@ -15,7 +15,7 @@ public class PistolItem extends Item { private final PistolAnimationDispatcher dispatcher; public PistolItem() { - super(new Properties()); + super(new Properties().stacksTo(1)); this.dispatcher = new PistolAnimationDispatcher(); } From ba2ccb59c49707bfec5ca26aad1fba7f495e4d21 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 16:39:34 -0400 Subject: [PATCH 19/40] Remove Optifine warning screen. --- .../client/screen/OptifineWarningScreen.java | 62 ------------------- .../mixins/AccessorWarningScreen.java | 15 ----- .../azurelib/util/IncompatibleModsCheck.java | 30 --------- .../mod/azure/azurelib/ClientListener.java | 3 - .../resources/azurelib.fabric.mixins.json | 1 - .../azure/azurelib/ClientNonModListener.java | 19 ------ .../main/resources/azurelib.forge.mixins.json | 1 - 7 files changed, 131 deletions(-) delete mode 100644 common/src/main/java/mod/azure/azurelib/client/screen/OptifineWarningScreen.java delete mode 100644 common/src/main/java/mod/azure/azurelib/mixins/AccessorWarningScreen.java delete mode 100644 common/src/main/java/mod/azure/azurelib/util/IncompatibleModsCheck.java delete mode 100644 neoforge/src/main/java/mod/azure/azurelib/ClientNonModListener.java diff --git a/common/src/main/java/mod/azure/azurelib/client/screen/OptifineWarningScreen.java b/common/src/main/java/mod/azure/azurelib/client/screen/OptifineWarningScreen.java deleted file mode 100644 index 702f31f07..000000000 --- a/common/src/main/java/mod/azure/azurelib/client/screen/OptifineWarningScreen.java +++ /dev/null @@ -1,62 +0,0 @@ -package mod.azure.azurelib.client.screen; - -import mod.azure.azurelib.mixins.AccessorWarningScreen; -import mod.azure.azurelib.platform.Services; -import net.minecraft.ChatFormatting; -import net.minecraft.Util; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.MultiLineLabel; -import net.minecraft.client.gui.screens.multiplayer.WarningScreen; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; - -public class OptifineWarningScreen extends WarningScreen { - public OptifineWarningScreen() { - super(HEADER, MESSAGE, CHECK_MESSAGE, NARRATED_TEXT); - } - - @Override - protected void initButtons(int yOffset) { - addRenderableWidget( - Button.builder(OPEN_MODS_FOLDER, buttonWidget -> Util.getPlatform().openFile(Services.PLATFORM.modsDir().toFile())) - .bounds(width / 2 - 155, 100 + yOffset, 150, 20) - .build() - ); - - addRenderableWidget( - Button.builder(OPTIFINE_ALTERNATIVES, buttonWidget -> Util.getPlatform().openUri( - "https://prismlauncher.org/wiki/getting-started/install-of-alternatives/" - )) - .bounds(width / 2 - 155 + 160, 100 + yOffset, 150, 20) - .build() - ); - - addRenderableWidget( - Button.builder(QUIT_GAME, buttonWidget -> this.minecraft.stop()) - .bounds(width / 2 - 75, 130 + yOffset, 150, 20) - .build() - ); - } - - @Override - protected void init() { - ((AccessorWarningScreen) this).setMessageText(MultiLineLabel.create(font, MESSAGE, width - 50)); - int yOffset = (((AccessorWarningScreen) this).getMessageText().getLineCount() + 1) * font.lineHeight * 2 - 20; - initButtons(yOffset); - } - - - @Override - public boolean shouldCloseOnEsc() { - return false; - } - - private static final MutableComponent HEADER = Component.translatable("header.azurelib.optifine").withStyle(ChatFormatting.DARK_RED, ChatFormatting.BOLD); - private static final Component MESSAGE = Component.translatable("message.azurelib.optifine"); - private static final Component CHECK_MESSAGE = Component.translatable("multiplayerWarning.check"); - private static final Component NARRATED_TEXT = HEADER.copy().append("\n").append(MESSAGE); - - private static final Component OPEN_MODS_FOLDER = Component.translatable("label.azurelib.open_mods_folder"); - private static final Component OPTIFINE_ALTERNATIVES = Component.translatable("label.azurelib.optifine_alternatives"); - private static final Component QUIT_GAME = Component.translatable("menu.quit"); -} diff --git a/common/src/main/java/mod/azure/azurelib/mixins/AccessorWarningScreen.java b/common/src/main/java/mod/azure/azurelib/mixins/AccessorWarningScreen.java deleted file mode 100644 index daa4f9adf..000000000 --- a/common/src/main/java/mod/azure/azurelib/mixins/AccessorWarningScreen.java +++ /dev/null @@ -1,15 +0,0 @@ -package mod.azure.azurelib.mixins; - -import net.minecraft.client.gui.components.MultiLineLabel; -import net.minecraft.client.gui.screens.multiplayer.WarningScreen; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(WarningScreen.class) -public interface AccessorWarningScreen { - @Accessor("message") - MultiLineLabel getMessageText(); - - @Accessor("message") - void setMessageText(MultiLineLabel messageText); -} diff --git a/common/src/main/java/mod/azure/azurelib/util/IncompatibleModsCheck.java b/common/src/main/java/mod/azure/azurelib/util/IncompatibleModsCheck.java deleted file mode 100644 index 9125aa7fd..000000000 --- a/common/src/main/java/mod/azure/azurelib/util/IncompatibleModsCheck.java +++ /dev/null @@ -1,30 +0,0 @@ -package mod.azure.azurelib.util; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.AzureLibMod; -import mod.azure.azurelib.client.screen.OptifineWarningScreen; -import net.minecraft.client.Minecraft; - -public class IncompatibleModsCheck { - public static boolean optifinePresent = false; - - public static void run() { - try { - Class.forName("net.optifine.Config"); - optifinePresent = true; - } catch (ClassNotFoundException e) { - optifinePresent = false; - } - } - - public static void warnings(Minecraft mc) { - if (IncompatibleModsCheck.optifinePresent) { - if (AzureLibMod.config.disableOptifineWarning) { - AzureLib.LOGGER.fatal("Optifine Has been detected, Disabled Warning Status: false"); - mc.setScreen(new OptifineWarningScreen()); - } else { - AzureLib.LOGGER.fatal("Optifine Has been detected, Disabled Warning Status: true"); - } - } - } -} diff --git a/fabric/src/main/java/mod/azure/azurelib/ClientListener.java b/fabric/src/main/java/mod/azure/azurelib/ClientListener.java index e3413e872..9e9b91fa9 100644 --- a/fabric/src/main/java/mod/azure/azurelib/ClientListener.java +++ b/fabric/src/main/java/mod/azure/azurelib/ClientListener.java @@ -9,10 +9,8 @@ import mod.azure.azurelib.testing.block.be.StargateBlockRenderer; import mod.azure.azurelib.testing.entity.MarauderRenderer; import mod.azure.azurelib.testing.item.PistolRenderer; -import mod.azure.azurelib.util.IncompatibleModsCheck; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; import net.minecraft.client.KeyMapping; @@ -25,7 +23,6 @@ public final class ClientListener implements ClientModInitializer { @Override public void onInitializeClient() { - ClientLifecycleEvents.CLIENT_STARTED.register(IncompatibleModsCheck::warnings); Keybindings.RELOAD = new KeyMapping("key.azurelib.reload", InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_R, "category.azurelib.binds"); KeyBindingHelper.registerKeyBinding(Keybindings.RELOAD); diff --git a/fabric/src/main/resources/azurelib.fabric.mixins.json b/fabric/src/main/resources/azurelib.fabric.mixins.json index 313c28917..2e48d5105 100644 --- a/fabric/src/main/resources/azurelib.fabric.mixins.json +++ b/fabric/src/main/resources/azurelib.fabric.mixins.json @@ -11,7 +11,6 @@ "PlayerListMixin" ], "client": [ - "AccessorWarningScreen", "BlockEntityMixin_AzBlockEntityAnimatorCache", "EntityMixin_AzEntityAnimatorCache", "ItemStackMixin_AzItemAnimatorCache", diff --git a/neoforge/src/main/java/mod/azure/azurelib/ClientNonModListener.java b/neoforge/src/main/java/mod/azure/azurelib/ClientNonModListener.java deleted file mode 100644 index a646c1081..000000000 --- a/neoforge/src/main/java/mod/azure/azurelib/ClientNonModListener.java +++ /dev/null @@ -1,19 +0,0 @@ -package mod.azure.azurelib; - -import mod.azure.azurelib.util.IncompatibleModsCheck; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.TitleScreen; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.ScreenEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; - -@Mod.EventBusSubscriber(Dist.CLIENT) -public class ClientNonModListener { - @SubscribeEvent - public static void onClientStart(ScreenEvent.Init.Post event) { - if (event.getScreen() instanceof TitleScreen) { - IncompatibleModsCheck.warnings(Minecraft.getInstance()); - } - } -} diff --git a/neoforge/src/main/resources/azurelib.forge.mixins.json b/neoforge/src/main/resources/azurelib.forge.mixins.json index 33b98a51e..0471c8e1b 100644 --- a/neoforge/src/main/resources/azurelib.forge.mixins.json +++ b/neoforge/src/main/resources/azurelib.forge.mixins.json @@ -12,7 +12,6 @@ "PlayerListMixin" ], "client": [ - "AccessorWarningScreen", "BlockEntityMixin_AzBlockEntityAnimatorCache", "EntityMixin_AzEntityAnimatorCache", "ItemStackMixin_AzItemAnimatorCache", From a6c4c15c116e3d88d87a4901e0e01ca60d0bae8f Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 16:39:41 -0400 Subject: [PATCH 20/40] Update Forge --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e9a176dbb..186316c7c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ minecraft_version = 1.20.1 archives_base_name = azurelib-1.20.1 # Forge -neo_version = 47.1.3 +neo_version = 47.4.0 loader_version_range = [47,) neo_version_range = [47.1,) minecraft_version_range = [1.20.1, 1.20.2) From d95e03e5770e01aa854973c59ab89c18cdd42448 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 18:06:18 -0400 Subject: [PATCH 21/40] Use ModDevGradle Legacy instead of VanillaGradle/ForgeGradle --- build.gradle | 120 +----- buildSrc/build.gradle | 3 + .../src/main/groovy/multiloader-common.gradle | 184 ++++++++ .../src/main/groovy/multiloader-loader.gradle | 44 ++ common/build.gradle | 50 ++- .../resources/META-INF/accesstransformer.cfg | 30 ++ common/src/main/resources/azurelib.aw | 1 + eclipse-formatter.xml | 401 ++++++++++++++++++ fabric/build.gradle | 100 +---- fabric/changelog.txt | 5 - gradle.properties | 3 +- gradle/wrapper/gradle-wrapper.properties | 2 +- neo/build.gradle | 96 +++++ .../mod/azure/azurelib/ClientModListener.java | 71 ++-- .../azure/azurelib/NeoForgeAzureLibMod.java | 138 ++++-- .../azure/azurelib/event/GeoRenderEvent.java | 240 +++++++++-- .../azurelib/items/NeoForgeAzureSpawnEgg.java | 24 ++ .../azurelib/mixins/ClientHooksMixin.java | 19 +- .../mixins/NeoMixinHumanoidArmorLayer.java | 47 +- .../mod/azure/azurelib/network/IPacket.java | 0 .../azure/azurelib/network/Networking.java | 28 +- .../azurelib/network/S2C_SendConfigData.java | 13 +- .../platform/AzureLibEventsNeoForge.java | 265 ++++++++++++ .../platform/NeoForgeAzureLibInitializer.java | 9 +- .../platform/NeoForgeAzureLibNetwork.java | 95 ++++- .../platform/NeoForgePlatformHelper.java | 14 +- .../azurelib/testing/block/StargateBlock.java | 11 +- .../be/StargateBlockAnimationDispatcher.java | 0 .../testing/block/be/StargateBlockEntity.java | 3 +- .../block/be/StargateBlockEntityAnimator.java | 6 +- .../block/be/StargateBlockRenderer.java | 3 +- .../src/main/resources/META-INF/mods.toml | 0 ...ure.azurelib.platform.services.AzureEvents | 0 ...elib.platform.services.AzureLibInitializer | 0 ...azurelib.platform.services.AzureLibNetwork | 0 ...azurelib.platform.services.IPlatformHelper | 0 .../main/resources/azurelib.forge.mixins.json | 0 neoforge/build.gradle | 170 -------- neoforge/changelog.txt | 3 - .../azurelib/items/NeoForgeAzureSpawnEgg.java | 20 - .../platform/AzureLibEventsNeoForge.java | 155 ------- .../resources/META-INF/accesstransformer.cfg | 15 - settings.gradle | 2 +- 43 files changed, 1602 insertions(+), 788 deletions(-) create mode 100644 buildSrc/build.gradle create mode 100644 buildSrc/src/main/groovy/multiloader-common.gradle create mode 100644 buildSrc/src/main/groovy/multiloader-loader.gradle create mode 100644 common/src/main/resources/META-INF/accesstransformer.cfg create mode 100644 eclipse-formatter.xml delete mode 100644 fabric/changelog.txt create mode 100644 neo/build.gradle rename {neoforge => neo}/src/main/java/mod/azure/azurelib/ClientModListener.java (64%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java (63%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/event/GeoRenderEvent.java (85%) create mode 100644 neo/src/main/java/mod/azure/azurelib/items/NeoForgeAzureSpawnEgg.java rename {neoforge => neo}/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java (77%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java (79%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/network/IPacket.java (100%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/network/Networking.java (72%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/network/S2C_SendConfigData.java (95%) create mode 100644 neo/src/main/java/mod/azure/azurelib/platform/AzureLibEventsNeoForge.java rename {neoforge => neo}/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibInitializer.java (92%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java (50%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/platform/NeoForgePlatformHelper.java (99%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java (92%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java (100%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java (99%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java (95%) rename {neoforge => neo}/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java (99%) rename {neoforge => neo}/src/main/resources/META-INF/mods.toml (100%) rename {neoforge => neo}/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureEvents (100%) rename {neoforge => neo}/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibInitializer (100%) rename {neoforge => neo}/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibNetwork (100%) rename {neoforge => neo}/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.IPlatformHelper (100%) rename {neoforge => neo}/src/main/resources/azurelib.forge.mixins.json (100%) delete mode 100644 neoforge/build.gradle delete mode 100644 neoforge/changelog.txt delete mode 100644 neoforge/src/main/java/mod/azure/azurelib/items/NeoForgeAzureSpawnEgg.java delete mode 100644 neoforge/src/main/java/mod/azure/azurelib/platform/AzureLibEventsNeoForge.java delete mode 100644 neoforge/src/main/resources/META-INF/accesstransformer.cfg diff --git a/build.gradle b/build.gradle index f4bd4d6e5..d8504aab8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,119 +1,5 @@ -import groovy.json.JsonOutput -import groovy.json.JsonSlurper - plugins { - id 'fabric-loom' version '1.2-SNAPSHOT' apply(false) - id 'net.minecraftforge.gradle' version '[6.0,6.2)' apply(false) - id 'org.spongepowered.gradle.vanilla' version '0.2.1-SNAPSHOT' apply(false) - id "org.spongepowered.mixin" version "0.7-SNAPSHOT" apply(false) -} - -version = version -group = maven_group - -subprojects { - apply plugin: 'java' - - java.toolchain.languageVersion = JavaLanguageVersion.of(17) - java.withSourcesJar() - java.withJavadocJar() - - jar { - from(rootProject.file("LICENSE")) { - rename { "${it}_${mod_name}" } - } - manifest { - attributes([ - "Specification-Title" : "AzureLib", - "Specification-Vendor" : "AzureDoom", - "Specification-Version" : "1", // We are version 1 of ourselves - "Implementation-Title" : project.name, - "Implementation-Version" : project.version, - "Implementation-Vendor" : "AzureDoom", - "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") - ]) - } - } - - sourcesJar { - from(rootProject.file("LICENSE")) { - rename { "${it}_${mod_name}" } - } - } - - repositories { - mavenCentral() - maven { - name = 'Sponge / Mixin' - url = 'https://repo.spongepowered.org/repository/maven-public/' - } - maven { - name = 'BlameJared Maven (JEI / CraftTweaker / Bookshelf)' - url = 'https://maven.blamejared.com' - } - } - - tasks.withType(JavaCompile).configureEach { - - it.options.encoding = 'UTF-8' - it.options.getRelease().set(17) - } - - processResources { - def expandProps = [ - "version" : project.version, - "group" : project.group, //Else we target the task's group. - "minecraft_version" : project.minecraft_version, - "neo_version" : project.neo_version, - "loader_version_range" : project.loader_version_range, - "neo_version_range" : project.neo_version_range, - "minecraft_version_range" : project.minecraft_version_range, - "fabric_version" : project.fabric_version, - "fabric_loader_version" : project.fabric_loader_version, - "mod_name" : project.mod_name, - "mod_author" : project.mod_author, - "mod_id" : project.mod_id, - "mod_license" : project.mod_license, - "mod_description" : project.mod_description, - "mod_credits" : project.mod_credits, - "mod_logo" : project.mod_logo, - "mod_url" : project.mod_url, - "mod_issues" : project.mod_issues, - "mod_sources" : project.mod_sources, - ] - - filesMatching(['pack.mcmeta', 'fabric.mod.json', '*.mixins.json', 'META-INF/mods.toml']) { - expand expandProps - } - inputs.properties(expandProps) - doLast { - def jsonMinifyStart = System.currentTimeMillis() - def jsonMinified = 0 - def jsonBytesSaved = 0 - fileTree(dir: outputs.files.asPath, include: '**/*.json').each { - File file = it - jsonMinified++ - def oldLength = file.length() - file.text = JsonOutput.toJson(new JsonSlurper().parse(file)) - jsonBytesSaved += oldLength - file.length() - } - println('Minified ' + jsonMinified + ' json files. Saved ' + jsonBytesSaved + ' bytes. Took ' + (System.currentTimeMillis() - jsonMinifyStart) + 'ms.') - } - } - - // Disables Gradle's custom module metadata from being published to maven. The - // metadata includes mapped dependencies which are not reasonably consumable by - // other mod developers. - tasks.withType(GenerateModuleMetadata).configureEach { - - enabled = false - } - - // Tells gradle to show 1000 errors instead of the default count of 100. - // See: https://stackoverflow.com/a/31905248 - gradle.projectsEvaluated { - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xmaxerrs" << "1000" - } - } + id 'fabric-loom' version '1.9-SNAPSHOT' apply(false) + id 'net.neoforged.moddev.legacyforge' version '2.0.77' apply(false) + id 'me.modmuss50.mod-publish-plugin' version "${modmuss50_mod_publish_version}" apply(false) } \ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 000000000..678405245 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,3 @@ +plugins { + id 'groovy-gradle-plugin' +} diff --git a/buildSrc/src/main/groovy/multiloader-common.gradle b/buildSrc/src/main/groovy/multiloader-common.gradle new file mode 100644 index 000000000..6892a737b --- /dev/null +++ b/buildSrc/src/main/groovy/multiloader-common.gradle @@ -0,0 +1,184 @@ +plugins { + id 'com.diffplug.spotless' + id 'java-library' + id 'maven-publish' + id 'idea' +} + +base { + archivesName = "${mod_id}-${project.name}-${minecraft_version}" +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(java_version) + withSourcesJar() + withJavadocJar() +} + +repositories { + mavenCentral() + // https://docs.gradle.org/current/userguide/declaring_repositories.html#declaring_content_exclusively_found_in_one_repository + exclusiveContent { + forRepository { + maven { + name = 'Sponge' + url = 'https://repo.spongepowered.org/repository/maven-public' + } + } + filter { includeGroupAndSubgroups('org.spongepowered') } + } + exclusiveContent { + forRepositories( + maven { + name = 'ParchmentMC' + url = 'https://maven.parchmentmc.org/' + }, + maven { + name = "NeoForge" + url = 'https://maven.neoforged.net/releases' + } + ) + filter { includeGroup('org.parchmentmc.data') } + } + maven { + name = 'BlameJared' + url = 'https://maven.blamejared.com' + } + maven { url "https://cfa2.cursemaven.com" } + maven { url "https://maven.terraformersmc.com/" } + maven { url "https://maven.terraformersmc.com/releases" } // modmenu +} + +dependencies { + implementation 'org.jetbrains:annotations:24.1.0' +} + +['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant -> + configurations."$variant".outgoing { + capability("$group:${project.name}:$version") + capability("$group:${base.archivesName.get()}:$version") + capability("$group:$mod_id-${project.name}-${minecraft_version}:$version") + capability("$group:$mod_id:$version") + } + publishing.publications.configureEach { + suppressPomMetadataWarningsFor(variant) + } +} + +sourcesJar { + from(rootProject.file('LICENSE')) { + rename { "${it}_${mod_name}" } + } +} + +jar { + from(rootProject.file('LICENSE')) { + rename { "${it}_${mod_name}" } + } + + manifest { + attributes([ + 'Specification-Title' : mod_name, + 'Specification-Vendor' : mod_author, + 'Specification-Version' : project.jar.archiveVersion, + 'Implementation-Title' : project.name, + 'Implementation-Version': project.jar.archiveVersion, + 'Implementation-Vendor' : mod_author, + 'Built-On-Minecraft' : minecraft_version + ]) + } +} + +processResources { + var expandProps = [ + "version" : project.version, + "group" : project.group, //Else we target the task's group. + "minecraft_version" : project.minecraft_version, + "neo_version" : project.neo_version, + "loader_version_range" : project.loader_version_range, + "neo_version_range" : project.neo_version_range, + "minecraft_version_range" : project.minecraft_version_range, + "fabric_version" : project.fabric_version, + "fabric_loader_version" : project.fabric_loader_version, + "mod_name" : project.mod_name, + "mod_author" : project.mod_author, + "mod_id" : project.mod_id, + "mod_license" : project.mod_license, + "mod_description" : project.mod_description, + "mod_credits" : project.mod_credits, + "mod_logo" : project.mod_logo, + "mod_url" : project.mod_url, + "mod_issues" : project.mod_issues, + "mod_sources" : project.mod_sources, + ] + + var jsonExpandProps = expandProps.collectEntries { + key, value -> [(key): value instanceof String ? value.replace("\n", "\\\\n") : value] + } + + filesMatching(['META-INF/mods.toml']) { + expand expandProps + } + + filesMatching(['pack.mcmeta', 'fabric.mod.json', '*.mixins.json']) { + expand jsonExpandProps + } + + inputs.properties(expandProps) +} + +publishing { + repositories { + maven { + name = project.mod_id + url = project.maven_url + credentials(PasswordCredentials) + authentication { + basic(BasicAuthentication) + } + } + } + publications { + maven(MavenPublication) { + artifactId base.archivesName.get() + from components.java + pom.withXml { + asNode().dependencies.dependency.each { dep -> + if (dep.groupId.text() == 'com.terraformersmc' && dep.artifactId.text() == 'modmenu') { + dep.parent().remove(dep) + } + } + } + } + } +} + + +// Disables Gradle's custom module metadata from being published to maven. The +// metadata includes mapped dependencies which are not reasonably consumable by +// other mod developers. +tasks.withType(GenerateModuleMetadata).configureEach { + enabled = false +} + +idea { + module { + downloadSources = true + downloadJavadoc = true + } +} + +spotless { + java { + eclipse().configFile("$rootDir/eclipse-formatter.xml") + endWithNewline() + importOrder("", "java", group.toString(), "\\#") + indentWithSpaces(4) + removeUnusedImports() + trimTrailingWhitespace() + } +} + +tasks.build { + dependsOn("spotlessApply") +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/multiloader-loader.gradle b/buildSrc/src/main/groovy/multiloader-loader.gradle new file mode 100644 index 000000000..92e23251e --- /dev/null +++ b/buildSrc/src/main/groovy/multiloader-loader.gradle @@ -0,0 +1,44 @@ +plugins { + id 'multiloader-common' +} + +configurations { + commonJava{ + canBeResolved = true + } + commonResources{ + canBeResolved = true + } +} + +dependencies { + compileOnly(project(':common')) { + capabilities { + requireCapability "$group:$mod_id" + } + } + commonJava project(path: ':common', configuration: 'commonJava') + commonResources project(path: ':common', configuration: 'commonResources') +} + +tasks.named('compileJava', JavaCompile) { + dependsOn(configurations.commonJava) + source(configurations.commonJava) +} + +processResources { + dependsOn(configurations.commonResources) + from(configurations.commonResources) +} + +tasks.named('javadoc', Javadoc).configure { + dependsOn(configurations.commonJava) + source(configurations.commonJava) +} + +tasks.named('sourcesJar', Jar) { + dependsOn(configurations.commonJava) + from(configurations.commonJava) + dependsOn(configurations.commonResources) + from(configurations.commonResources) +} diff --git a/common/build.gradle b/common/build.gradle index 25080ca95..b5f367c8c 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,17 +1,18 @@ plugins { - id 'idea' - id 'java' - id 'maven-publish' - id 'org.spongepowered.gradle.vanilla' -} -base { - archivesName = "${mod_id}-common-${minecraft_version}" + id 'multiloader-common' + id 'net.neoforged.moddev.legacyforge' + id 'com.diffplug.spotless' version "7.0.0.BETA3" } -minecraft { - version(minecraft_version) - if (file("src/main/resources/${mod_id}.aw").exists()) { - accessWideners(file("src/main/resources/${mod_id}.aw")) +legacyForge { + mcpVersion = minecraft_version + def at = file('src/main/resources/META-INF/accesstransformer.cfg') + if (at.exists()) { + accessTransformers.from(at.absolutePath) + } + parchment { + minecraftVersion = parchment_minecraft + mappingsVersion = parchment_version } } @@ -21,21 +22,18 @@ dependencies { annotationProcessor group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.3.5' } -publishing { - repositories { - maven { - name = "azurelib" - url = "https://maven.azuredoom.com/mods" - credentials(PasswordCredentials) - authentication { - basic(BasicAuthentication) - } - } +configurations { + commonJava { + canBeResolved = false + canBeConsumed = true } - publications { - maven(MavenPublication) { - artifactId base.archivesName.get() - from components.java - } + commonResources { + canBeResolved = false + canBeConsumed = true } +} + +artifacts { + commonJava sourceSets.main.java.sourceDirectories.singleFile + commonResources sourceSets.main.resources.sourceDirectories.singleFile } \ No newline at end of file diff --git a/common/src/main/resources/META-INF/accesstransformer.cfg b/common/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 000000000..66e689e1d --- /dev/null +++ b/common/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1,30 @@ +public net.minecraft.sounds.SoundEvent m_262856_(Lnet/minecraft/resources/ResourceLocation;F)Lnet/minecraft/sounds/SoundEvent; # createFixedRangeEvent +public net.minecraft.world.entity.Entity f_19857_ # yRot +public net.minecraft.world.entity.Entity f_19858_ # xRot +public net.minecraft.world.entity.Entity m_7939_()Lnet/minecraft/world/phys/Vec3; # getLeashOffset + +public net.minecraft.client.model.geom.ModelPart$Cube +public net.minecraft.client.model.geom.ModelPart f_104212_ # cubes +public net.minecraft.client.renderer.LevelRenderer m_109817_()Z # shouldShowEntityOutlines +public net.minecraft.client.renderer.entity.EntityRenderDispatcher f_114362_ # renderers +public net.minecraft.client.renderer.RenderStateShard$ShaderStateShard +public net.minecraft.client.renderer.RenderStateShard$TextureStateShard +public net.minecraft.client.renderer.RenderStateShard$TransparencyStateShard +public net.minecraft.client.renderer.RenderStateShard$WriteMaskStateShard +public net.minecraft.client.renderer.RenderType$CompositeState + +public net.minecraft.world.entity.WalkAnimationState f_267406_ # speedOld + +public net.minecraft.client.renderer.RenderType m_173209_(Ljava/lang/String;Lcom/mojang/blaze3d/vertex/VertexFormat;Lcom/mojang/blaze3d/vertex/VertexFormat$Mode;ILnet/minecraft/client/renderer/RenderType$CompositeState;)Lnet/minecraft/client/renderer/RenderType$CompositeRenderType; # create +public net.minecraft.client.renderer.RenderType m_173215_(Ljava/lang/String;Lcom/mojang/blaze3d/vertex/VertexFormat;Lcom/mojang/blaze3d/vertex/VertexFormat$Mode;IZZLnet/minecraft/client/renderer/RenderType$CompositeState;)Lnet/minecraft/client/renderer/RenderType$CompositeRenderType; # create + +public net.minecraft.client.model.AgeableListModel f_102007_ # scaleHead +public net.minecraft.client.model.AgeableListModel f_170338_ # babyYHeadOffset +public net.minecraft.client.model.AgeableListModel f_170339_ # babyZHeadOffset +public net.minecraft.client.model.AgeableListModel f_102010_ # babyHeadScale +public net.minecraft.client.model.AgeableListModel f_102011_ # babyBodyScale +public net.minecraft.client.model.AgeableListModel f_102012_ # bodyYOffset + +public-f net.minecraft.client.renderer.LevelRenderer f_109464_ # renderBuffers +public-f net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer m_289609_(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/HumanoidModel;ZFFFLjava/lang/String;)V # renderModel +public com.mojang.blaze3d.vertex.BufferBuilder f_85661_ # building \ No newline at end of file diff --git a/common/src/main/resources/azurelib.aw b/common/src/main/resources/azurelib.aw index 2d6102d7a..d38bf4f04 100644 --- a/common/src/main/resources/azurelib.aw +++ b/common/src/main/resources/azurelib.aw @@ -4,6 +4,7 @@ accessible field net/minecraft/world/entity/Entity yRot F accessible field net/minecraft/world/entity/Entity xRot F accessible method net/minecraft/world/entity/Entity getLeashOffset ()Lnet/minecraft/world/phys/Vec3; +accessible class net/minecraft/client/model/geom/ModelPart$Cube accessible field net/minecraft/client/model/geom/ModelPart cubes Ljava/util/List; accessible method net/minecraft/client/renderer/LevelRenderer shouldShowEntityOutlines ()Z accessible field net/minecraft/client/renderer/entity/EntityRenderDispatcher renderers Ljava/util/Map; diff --git a/eclipse-formatter.xml b/eclipse-formatter.xml new file mode 100644 index 000000000..6bc8eedd2 --- /dev/null +++ b/eclipse-formatter.xml @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fabric/build.gradle b/fabric/build.gradle index 0e7e4723a..f733dcafd 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,40 +1,20 @@ -import groovy.json.JsonOutput -import groovy.json.JsonSlurper - plugins { - id 'java' - id 'idea' - id 'maven-publish' + id 'multiloader-loader' id 'fabric-loom' - id "me.modmuss50.mod-publish-plugin" version "0.8.4" -} - -base { - archivesName = "${mod_id}-fabric-${minecraft_version}" -} - -repositories { - mavenCentral() - maven { url 'https://dl.cloudsmith.io/public/azuredoom-mods/azurelib/maven/' } - maven { url 'https://maven.blamejared.com' } - maven { url 'https://api.modrinth.com/maven' } - maven { url "https://cfa2.cursemaven.com" } - maven { url "https://maven.cloudsmith.io/azuredoom-mods/azurelib/" } - maven { url "https://plugins.gradle.org/m2/" } - maven { url "https://maven.shedaniel.me/" } - maven { url "https://maven.terraformersmc.com/" } - maven { url "https://maven.terraformersmc.com/releases" } // modmenu + id 'me.modmuss50.mod-publish-plugin' + id 'com.diffplug.spotless' version "7.0.0.BETA3" } dependencies { minecraft "com.mojang:minecraft:${minecraft_version}" - mappings loom.officialMojangMappings() + mappings loom.layered { + officialMojangMappings() + parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") + } modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}" - compileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.1' modApi "com.terraformersmc:modmenu:${modmenu_version}" include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:0.4.1"))) - compileOnly project(":common") } loom { @@ -60,71 +40,11 @@ loom { } } -tasks.withType(JavaCompile).configureEach { - source(project(":common").sourceSets.main.allSource) -} - -tasks.withType(Javadoc).configureEach { - source(project(":common").sourceSets.main.allJava) -} - -tasks.named("sourcesJar", Jar) { - from(project(":common").sourceSets.main.allSource) -} - -processResources { - from project(":common").sourceSets.main.resources -} - -// TODO: Everything below here might be fine to move to the top-level build.gradle file. -// They were originally in the previous fabric build.gradle. - bvanseg - -tasks.withType(JavaCompile).configureEach { - it.options.release = 17 -} - -jar { - from "LICENSE" -} - -publishing { - repositories { - maven { - name = "azurelib" - url = "https://maven.azuredoom.com/mods" - credentials(PasswordCredentials) - authentication { - basic(BasicAuthentication) - } - } - } - publications { - maven(MavenPublication) { - artifactId base.archivesName.get() - from components.java - pom.withXml { - asNode().dependencies.dependency.each { dep -> - if (dep.groupId.text() == 'com.terraformersmc' && dep.artifactId.text() == 'modmenu') { - dep.parent().remove(dep) - } - } - } - } - } -} - -// Disables Gradle's custom module metadata from being published to maven. The -// metadata includes mapped dependencies which are not reasonably consumable by -// other mod developers. -tasks.withType(GenerateModuleMetadata).configureEach { - enabled = false -} - -if (file('key.properties').exists()) { +def keyPropsFile = new File(rootProject.projectDir, "key.properties") +if (keyPropsFile.exists()) { publishMods { def releaseProp = new Properties() - File secretPropsFile = file("key.properties") - releaseProp.load(secretPropsFile.newInputStream()) + releaseProp.load(new FileInputStream(keyPropsFile)) file = remapJar.archiveFile changelog = rootProject.file("changelog.md").text type = STABLE diff --git a/fabric/changelog.txt b/fabric/changelog.txt deleted file mode 100644 index 6e4e66b86..000000000 --- a/fabric/changelog.txt +++ /dev/null @@ -1,5 +0,0 @@ -v1.0.33 - -- Updated Fabric name -- Add Modmenu badge -- Tweak MixinHumanoidArmorLayer \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 186316c7c..19327fa1c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,8 +3,7 @@ # Every field you add must be added to the root build.gradle expandProps map. # Gradle -org.gradle.jvmargs = -Xmx3G -org.gradle.daemon = false +org.gradle.jvmargs = -Xmx3G # Project group = mod.azure.azurelib diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4413138c..94113f200 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.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/neo/build.gradle b/neo/build.gradle new file mode 100644 index 000000000..343bb1f85 --- /dev/null +++ b/neo/build.gradle @@ -0,0 +1,96 @@ +plugins { + id 'multiloader-loader' + id 'net.neoforged.moddev.legacyforge' + id 'me.modmuss50.mod-publish-plugin' + id 'com.diffplug.spotless' version "7.0.0.BETA3" +} + +mixin { + add(sourceSets.main, "${mod_id}.refmap.json") + + config("${mod_id}.mixins.json") + config("${mod_id}.forge.mixins.json") +} + +legacyForge { + version = "${minecraft_version}-${neo_version}" + + def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg') + if (at.exists()) { + accessTransformers.from(at.absolutePath) + } + parchment { + minecraftVersion = parchment_minecraft + mappingsVersion = parchment_version + } + runs { + client { + client() + } + data { + data() + programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() + } + server { + server() + } + } + + mods { + "${mod_id}" { + sourceSet sourceSets.main + } + } +} + +sourceSets.main.resources.srcDir 'src/generated/resources' + +dependencies { + annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT:processor") + compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")) + jarJar(implementation("io.github.llamalad7:mixinextras-forge:0.4.1")) +} + +jar { + finalizedBy('reobfJar') + manifest.attributes([ + "MixinConfigs": "${mod_id}.mixins.json,${mod_id}.forge.mixins.json" + ]) +} + +def keyPropsFile = new File(rootProject.projectDir, "key.properties") +if (keyPropsFile.exists()) { + publishMods { + def releaseProp = new Properties() + releaseProp.load(new FileInputStream(keyPropsFile)) + file = jar.archiveFile + changelog = rootProject.file("changelog.md").text + type = STABLE + modLoaders.add("neoforge") + modLoaders.add("forge") + + discord { + webhookUrl = releaseProp.getProperty("discordKey") + dryRunWebhookUrl = releaseProp.getProperty("discordKey") + username = "AzureBot" + avatarUrl = "https://cdn.modrinth.com/data/7zlUOZvb/8b2cb16452c1d3bb33574519a6f5cf4ce86f13a3_96.webp" + content = changelog.map { "# A new version of AzureLib for Forge has been released for 1.20.1 \n" + it} + setPlatforms(publishMods.platforms.curseforge, publishMods.platforms.modrinth) + style { + look = "MODERN" + thumbnailUrl = "https://cdn.modrinth.com/data/7zlUOZvb/8b2cb16452c1d3bb33574519a6f5cf4ce86f13a3_96.webp" + } + } + curseforge { + projectId = "817423" + projectSlug = "azurelib" + accessToken = releaseProp.getProperty("curseKey") + minecraftVersions.add(project.minecraft_version) + } + modrinth { + projectId = "7zlUOZvb" + accessToken = releaseProp.getProperty('modrinthKey') + minecraftVersions.add(project.minecraft_version) + } + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/mod/azure/azurelib/ClientModListener.java b/neo/src/main/java/mod/azure/azurelib/ClientModListener.java similarity index 64% rename from neoforge/src/main/java/mod/azure/azurelib/ClientModListener.java rename to neo/src/main/java/mod/azure/azurelib/ClientModListener.java index 3be5705bb..813ef241f 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/ClientModListener.java +++ b/neo/src/main/java/mod/azure/azurelib/ClientModListener.java @@ -1,14 +1,6 @@ package mod.azure.azurelib; import com.mojang.blaze3d.platform.InputConstants; -import mod.azure.azurelib.client.AzureLibClient; -import mod.azure.azurelib.config.ConfigHolder; -import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; -import mod.azure.azurelib.rewrite.render.item.AzItemRendererRegistry; -import mod.azure.azurelib.testing.armor.DoomicornArmorRenderer; -import mod.azure.azurelib.testing.block.be.StargateBlockRenderer; -import mod.azure.azurelib.testing.entity.MarauderRenderer; -import mod.azure.azurelib.testing.item.PistolRenderer; import net.minecraft.client.KeyMapping; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraftforge.api.distmarker.Dist; @@ -26,6 +18,15 @@ import java.util.Map; import java.util.Optional; +import mod.azure.azurelib.client.AzureLibClient; +import mod.azure.azurelib.config.ConfigHolder; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; +import mod.azure.azurelib.rewrite.render.item.AzItemRendererRegistry; +import mod.azure.azurelib.testing.armor.DoomicornArmorRenderer; +import mod.azure.azurelib.testing.block.be.StargateBlockRenderer; +import mod.azure.azurelib.testing.entity.MarauderRenderer; +import mod.azure.azurelib.testing.item.PistolRenderer; + @EventBusSubscriber(modid = AzureLib.MOD_ID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) public class ClientModListener { @@ -33,11 +34,11 @@ public class ClientModListener { public static void onClientSetup(final FMLClientSetupEvent event) { AzItemRendererRegistry.register(NeoForgeAzureLibMod.AzureItems.PISTOL_ITEM.get(), PistolRenderer::new); AzArmorRendererRegistry.register( - DoomicornArmorRenderer::new, - NeoForgeAzureLibMod.AzureItems.DOOMICORN_HELMET.get(), - NeoForgeAzureLibMod.AzureItems.DOOMICORN_CHESTPLATE.get(), - NeoForgeAzureLibMod.AzureItems.DOOMICORN_LEGGINGS.get(), - NeoForgeAzureLibMod.AzureItems.DOOMICORN_BOOTS.get() + DoomicornArmorRenderer::new, + NeoForgeAzureLibMod.AzureItems.DOOMICORN_HELMET.get(), + NeoForgeAzureLibMod.AzureItems.DOOMICORN_CHESTPLATE.get(), + NeoForgeAzureLibMod.AzureItems.DOOMICORN_LEGGINGS.get(), + NeoForgeAzureLibMod.AzureItems.DOOMICORN_BOOTS.get() ); } @@ -45,21 +46,33 @@ public static void onClientSetup(final FMLClientSetupEvent event) { public static void registerRenderers(final EntityRenderersEvent.RegisterRenderers event) { event.registerEntityRenderer(NeoForgeAzureLibMod.AzureEntities.MARAUDER.get(), MarauderRenderer::new); event.registerBlockEntityRenderer( - NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), - (BlockEntityRendererProvider.Context rendererDispatcherIn) -> new StargateBlockRenderer() + NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), + (BlockEntityRendererProvider.Context rendererDispatcherIn) -> new StargateBlockRenderer() ); } @SubscribeEvent public static void registerKeys(final RegisterKeyMappingsEvent event) { - Keybindings.RELOAD = new KeyMapping("key.azurelib.reload", InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_R, - "category.azurelib.binds"); + Keybindings.RELOAD = new KeyMapping( + "key.azurelib.reload", + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_R, + "category.azurelib.binds" + ); event.register(Keybindings.RELOAD); - Keybindings.SCOPE = new KeyMapping("key.azurelib.scope", InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_LEFT_ALT, - "category.azurelib.binds"); + Keybindings.SCOPE = new KeyMapping( + "key.azurelib.scope", + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_LEFT_ALT, + "category.azurelib.binds" + ); event.register(Keybindings.SCOPE); - Keybindings.FIRE_WEAPON = new KeyMapping("key.azurelib.fire", InputConstants.Type.KEYSYM, - GLFW.GLFW_KEY_UNKNOWN, "category.azurelib.binds"); + Keybindings.FIRE_WEAPON = new KeyMapping( + "key.azurelib.fire", + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_UNKNOWN, + "category.azurelib.binds" + ); event.register(Keybindings.FIRE_WEAPON); } @@ -72,13 +85,15 @@ public static void clientInit(final FMLClientSetupEvent event) { Optional optional = modList.getModContainerById(modId); optional.ifPresent(modContainer -> { List> list = entry.getValue(); - modContainer.registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class, - () -> new ConfigScreenHandler.ConfigScreenFactory((minecraft, screen) -> { - if (list.size() == 1) { - return AzureLibClient.getConfigScreen(list.get(0).getConfigId(), screen); - } - return AzureLibClient.getConfigScreenByGroup(list, modId, screen); - })); + modContainer.registerExtensionPoint( + ConfigScreenHandler.ConfigScreenFactory.class, + () -> new ConfigScreenHandler.ConfigScreenFactory((minecraft, screen) -> { + if (list.size() == 1) { + return AzureLibClient.getConfigScreen(list.get(0).getConfigId(), screen); + } + return AzureLibClient.getConfigScreenByGroup(list, modId, screen); + }) + ); }); } } diff --git a/neoforge/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java b/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java similarity index 63% rename from neoforge/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java rename to neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java index 146a0984a..b8a71ea48 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java +++ b/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java @@ -1,26 +1,11 @@ package mod.azure.azurelib; -import mod.azure.azurelib.config.AzureLibConfig; -import mod.azure.azurelib.config.format.ConfigFormats; -import mod.azure.azurelib.config.io.ConfigIO; -import mod.azure.azurelib.enchantments.IncendiaryEnchantment; -import mod.azure.azurelib.entities.TickingLightBlock; -import mod.azure.azurelib.entities.TickingLightEntity; -import mod.azure.azurelib.network.Networking; -import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; -import mod.azure.azurelib.testing.armor.DoomicornArmor; -import mod.azure.azurelib.testing.block.StargateBlock; -import mod.azure.azurelib.testing.block.be.StargateBlockEntity; -import mod.azure.azurelib.testing.entity.MarauderEntity; -import mod.azure.azurelib.testing.item.PistolItem; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.entity.monster.Monster; import net.minecraft.world.item.ArmorItem; import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -33,6 +18,20 @@ import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; +import mod.azure.azurelib.config.AzureLibConfig; +import mod.azure.azurelib.config.format.ConfigFormats; +import mod.azure.azurelib.config.io.ConfigIO; +import mod.azure.azurelib.enchantments.IncendiaryEnchantment; +import mod.azure.azurelib.entities.TickingLightBlock; +import mod.azure.azurelib.entities.TickingLightEntity; +import mod.azure.azurelib.network.Networking; +import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; +import mod.azure.azurelib.testing.armor.DoomicornArmor; +import mod.azure.azurelib.testing.block.StargateBlock; +import mod.azure.azurelib.testing.block.be.StargateBlockEntity; +import mod.azure.azurelib.testing.entity.MarauderEntity; +import mod.azure.azurelib.testing.item.PistolItem; + @Mod.EventBusSubscriber @Mod(AzureLib.MOD_ID) public final class NeoForgeAzureLibMod { @@ -60,40 +59,103 @@ private void init(FMLCommonSetupEvent event) { } public class AzureEnchantments { - public static final DeferredRegister ENCHANTMENTS = DeferredRegister.create(ForgeRegistries.ENCHANTMENTS, AzureLib.MOD_ID); - public static final RegistryObject INCENDIARYENCHANTMENT = ENCHANTMENTS.register("incendiaryenchantment", () -> new IncendiaryEnchantment(Enchantment.Rarity.RARE, EquipmentSlot.MAINHAND)); + + public static final DeferredRegister ENCHANTMENTS = DeferredRegister.create( + ForgeRegistries.ENCHANTMENTS, + AzureLib.MOD_ID + ); + + public static final RegistryObject INCENDIARYENCHANTMENT = ENCHANTMENTS.register( + "incendiaryenchantment", + () -> new IncendiaryEnchantment(Enchantment.Rarity.RARE, EquipmentSlot.MAINHAND) + ); } public class AzureBlocks { - public static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, AzureLib.MOD_ID); - public static final RegistryObject TICKING_LIGHT_BLOCK = BLOCKS.register("lightblock", TickingLightBlock::new); + public static final DeferredRegister BLOCKS = DeferredRegister.create( + ForgeRegistries.BLOCKS, + AzureLib.MOD_ID + ); + + public static final RegistryObject TICKING_LIGHT_BLOCK = BLOCKS.register( + "lightblock", + TickingLightBlock::new + ); + public static final RegistryObject STARGATE_BLOCK = BLOCKS.register("stargate", StargateBlock::new); } public class AzureEntities { - public static final DeferredRegister> TILE_TYPES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, AzureLib.MOD_ID); - public static final DeferredRegister> ENTITY_TYPES = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, AzureLib.MOD_ID); + public static final DeferredRegister> TILE_TYPES = DeferredRegister.create( + ForgeRegistries.BLOCK_ENTITY_TYPES, + AzureLib.MOD_ID + ); + + public static final DeferredRegister> ENTITY_TYPES = DeferredRegister.create( + ForgeRegistries.ENTITY_TYPES, + AzureLib.MOD_ID + ); - public static final RegistryObject> TICKING_LIGHT_ENTITY = TILE_TYPES.register("lightblock", () -> BlockEntityType.Builder.of(TickingLightEntity::new, AzureBlocks.TICKING_LIGHT_BLOCK.get()).build(null)); - public static final RegistryObject> STARGATE_BLOCK_ENTITY = TILE_TYPES.register("stargate", () -> BlockEntityType.Builder.of(StargateBlockEntity::new, AzureBlocks.STARGATE_BLOCK.get()).build(null)); - public static final RegistryObject> MARAUDER = ENTITY_TYPES.register("maruader", () -> EntityType.Builder.of(MarauderEntity::new, MobCategory.MONSTER).sized(0.6F, 1.95F).clientTrackingRange(8).build(AzureLib.MOD_ID + ":marauder")); + public static final RegistryObject> TICKING_LIGHT_ENTITY = TILE_TYPES + .register( + "lightblock", + () -> BlockEntityType.Builder.of(TickingLightEntity::new, AzureBlocks.TICKING_LIGHT_BLOCK.get()) + .build(null) + ); + + public static final RegistryObject> STARGATE_BLOCK_ENTITY = TILE_TYPES + .register( + "stargate", + () -> BlockEntityType.Builder.of(StargateBlockEntity::new, AzureBlocks.STARGATE_BLOCK.get()).build(null) + ); + + public static final RegistryObject> MARAUDER = ENTITY_TYPES.register( + "maruader", + () -> EntityType.Builder.of(MarauderEntity::new, MobCategory.MONSTER) + .sized(0.6F, 1.95F) + .clientTrackingRange(8) + .build(AzureLib.MOD_ID + ":marauder") + ); } public class AzureItems { - public static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, AzureLib.MOD_ID); + + public static final DeferredRegister ITEMS = DeferredRegister.create( + ForgeRegistries.ITEMS, + AzureLib.MOD_ID + ); public static final RegistryObject PISTOL_ITEM = ITEMS.register("pistol", () -> new PistolItem()); - public static final RegistryObject DOOMICORN_HELMET = ITEMS.register("doomicorn_helmet", () -> new DoomicornArmor( - ArmorItem.Type.HELMET)); - public static final RegistryObject DOOMICORN_CHESTPLATE = ITEMS.register("doomicorn_chestplate", () -> new DoomicornArmor( - ArmorItem.Type.CHESTPLATE)); - public static final RegistryObject DOOMICORN_LEGGINGS = ITEMS.register("doomicorn_leggings", () -> new DoomicornArmor( - ArmorItem.Type.LEGGINGS)); - public static final RegistryObject DOOMICORN_BOOTS = ITEMS.register("doomicorn_boots", () -> new DoomicornArmor( - ArmorItem.Type.BOOTS)); + public static final RegistryObject DOOMICORN_HELMET = ITEMS.register( + "doomicorn_helmet", + () -> new DoomicornArmor( + ArmorItem.Type.HELMET + ) + ); + + public static final RegistryObject DOOMICORN_CHESTPLATE = ITEMS.register( + "doomicorn_chestplate", + () -> new DoomicornArmor( + ArmorItem.Type.CHESTPLATE + ) + ); + + public static final RegistryObject DOOMICORN_LEGGINGS = ITEMS.register( + "doomicorn_leggings", + () -> new DoomicornArmor( + ArmorItem.Type.LEGGINGS + ) + ); + + public static final RegistryObject DOOMICORN_BOOTS = ITEMS.register( + "doomicorn_boots", + () -> new DoomicornArmor( + ArmorItem.Type.BOOTS + ) + ); } public void createEntityAttributes(final EntityAttributeCreationEvent event) { @@ -102,11 +164,11 @@ public void createEntityAttributes(final EntityAttributeCreationEvent event) { public void commonSetup(final FMLCommonSetupEvent event) { AzIdentityRegistry.register( - AzureItems.PISTOL_ITEM.get(), - AzureItems.DOOMICORN_HELMET.get(), - AzureItems.DOOMICORN_CHESTPLATE.get(), - AzureItems.DOOMICORN_LEGGINGS.get(), - AzureItems.DOOMICORN_BOOTS.get() + AzureItems.PISTOL_ITEM.get(), + AzureItems.DOOMICORN_HELMET.get(), + AzureItems.DOOMICORN_CHESTPLATE.get(), + AzureItems.DOOMICORN_LEGGINGS.get(), + AzureItems.DOOMICORN_BOOTS.get() ); } } diff --git a/neoforge/src/main/java/mod/azure/azurelib/event/GeoRenderEvent.java b/neo/src/main/java/mod/azure/azurelib/event/GeoRenderEvent.java similarity index 85% rename from neoforge/src/main/java/mod/azure/azurelib/event/GeoRenderEvent.java rename to neo/src/main/java/mod/azure/azurelib/event/GeoRenderEvent.java index 4bd6137f8..51843281a 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/event/GeoRenderEvent.java +++ b/neo/src/main/java/mod/azure/azurelib/event/GeoRenderEvent.java @@ -1,12 +1,6 @@ package mod.azure.azurelib.event; import com.mojang.blaze3d.vertex.PoseStack; -import mod.azure.azurelib.animatable.GeoItem; -import mod.azure.azurelib.animatable.GeoReplacedEntity; -import mod.azure.azurelib.cache.object.BakedGeoModel; -import mod.azure.azurelib.core.animatable.GeoAnimatable; -import mod.azure.azurelib.renderer.*; -import mod.azure.azurelib.renderer.layer.GeoRenderLayer; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.item.ItemStack; @@ -15,7 +9,15 @@ import net.minecraftforge.eventbus.api.Event; import org.jetbrains.annotations.Nullable; +import mod.azure.azurelib.animatable.GeoItem; +import mod.azure.azurelib.animatable.GeoReplacedEntity; +import mod.azure.azurelib.cache.object.BakedGeoModel; +import mod.azure.azurelib.core.animatable.GeoAnimatable; +import mod.azure.azurelib.renderer.*; +import mod.azure.azurelib.renderer.layer.GeoRenderLayer; + public interface GeoRenderEvent { + /** * Returns the renderer for this event * @@ -32,6 +34,7 @@ public interface GeoRenderEvent { * Renderer events for armor pieces being rendered by {@link GeoArmorRenderer} */ abstract class Armor extends Event implements GeoRenderEvent { + private final GeoArmorRenderer renderer; public Armor(GeoArmorRenderer renderer) { @@ -76,17 +79,30 @@ public EquipmentSlot getEquipmentSlot() { * This event is called before rendering, but after {@link GeoRenderer#preRender} *

* This event is Cancelable
- * If the event is cancelled, the armor piece will not be rendered and the corresponding {@link Post} event will not be fired. + * If the event is cancelled, the armor piece will not be rendered and the corresponding {@link Post} event will + * not be fired. */ @Cancelable public static class Pre extends Armor { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Pre(GeoArmorRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Pre( + GeoArmorRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -123,13 +139,25 @@ public int getPackedLight() { * This event is called after {@link GeoRenderer#postRender} */ public static class Post extends Armor { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Post(GeoArmorRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Post( + GeoArmorRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -166,6 +194,7 @@ public int getPackedLight() { * Use this event to add render layers to the renderer as needed */ public static class CompileRenderLayers extends Armor { + public CompileRenderLayers(GeoArmorRenderer renderer) { super(renderer); } @@ -173,7 +202,8 @@ public CompileRenderLayers(GeoArmorRenderer renderer) { /** * Adds a {@link GeoRenderLayer} to the renderer *

- * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and renderer + * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and + * renderer */ public void addLayer(GeoRenderLayer renderLayer) { getRenderer().addRenderLayer(renderLayer); @@ -185,6 +215,7 @@ public void addLayer(GeoRenderLayer renderLayer) { * Renderer events for {@link BlockEntity BlockEntities} being rendered by {@link GeoBlockRenderer} */ abstract class Block extends Event implements GeoRenderEvent { + private final GeoBlockRenderer renderer; public Block(GeoBlockRenderer renderer) { @@ -212,17 +243,30 @@ public BlockEntity getBlockEntity() { * This event is called before rendering, but after {@link GeoRenderer#preRender} *

* This event is Cancelable
- * If the event is cancelled, the block entity will not be rendered and the corresponding {@link Post} event will not be fired. + * If the event is cancelled, the block entity will not be rendered and the corresponding {@link Post} event + * will not be fired. */ @Cancelable public static class Pre extends Block { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Pre(GeoBlockRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Pre( + GeoBlockRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -259,13 +303,25 @@ public int getPackedLight() { * This event is called after {@link GeoRenderer#postRender} */ public static class Post extends Block { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Post(GeoBlockRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Post( + GeoBlockRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -302,6 +358,7 @@ public int getPackedLight() { * Use this event to add render layers to the renderer as needed */ public static class CompileRenderLayers extends Block { + public CompileRenderLayers(GeoBlockRenderer renderer) { super(renderer); } @@ -309,7 +366,8 @@ public CompileRenderLayers(GeoBlockRenderer renderer) { /** * Adds a {@link GeoRenderLayer} to the renderer *

- * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and renderer + * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and + * renderer */ public void addLayer(GeoRenderLayer renderLayer) { getRenderer().addRenderLayer(renderLayer); @@ -318,9 +376,11 @@ public void addLayer(GeoRenderLayer renderLayer) { } /** - * Renderer events for {@link net.minecraft.world.entity.Entity Entities} being rendered by {@link GeoEntityRenderer} + * Renderer events for {@link net.minecraft.world.entity.Entity Entities} being rendered by + * {@link GeoEntityRenderer} */ abstract class Entity extends Event implements GeoRenderEvent { + private final GeoEntityRenderer renderer; public Entity(GeoEntityRenderer renderer) { @@ -348,17 +408,30 @@ public net.minecraft.world.entity.Entity getEntity() { * This event is called before rendering, but after {@link GeoRenderer#preRender} *

* This event is Cancelable
- * If the event is cancelled, the entity will not be rendered and the corresponding {@link Post} event will not be fired. + * If the event is cancelled, the entity will not be rendered and the corresponding {@link Post} event will not + * be fired. */ @Cancelable public static class Pre extends Entity { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Pre(GeoEntityRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Pre( + GeoEntityRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -395,13 +468,25 @@ public int getPackedLight() { * This event is called after {@link GeoRenderer#postRender} */ public static class Post extends Entity { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Post(GeoEntityRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Post( + GeoEntityRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -438,6 +523,7 @@ public int getPackedLight() { * Use this event to add render layers to the renderer as needed */ public static class CompileRenderLayers extends Entity { + public CompileRenderLayers(GeoEntityRenderer renderer) { super(renderer); } @@ -445,7 +531,8 @@ public CompileRenderLayers(GeoEntityRenderer renderer) { /** * Adds a {@link GeoRenderLayer} to the renderer *

- * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and renderer + * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and + * renderer */ public void addLayer(GeoRenderLayer renderLayer) { getRenderer().addRenderLayer(renderLayer); @@ -457,6 +544,7 @@ public void addLayer(GeoRenderLayer renderLayer) { * Renderer events for {@link ItemStack Items} being rendered by {@link GeoItemRenderer} */ abstract class Item extends Event implements GeoRenderEvent { + private final GeoItemRenderer renderer; public Item(GeoItemRenderer renderer) { @@ -484,17 +572,30 @@ public ItemStack getItemStack() { * This event is called before rendering, but after {@link GeoRenderer#preRender} *

* This event is Cancelable
- * If the event is cancelled, the ItemStack will not be rendered and the corresponding {@link Post} event will not be fired. + * If the event is cancelled, the ItemStack will not be rendered and the corresponding {@link Post} event will + * not be fired. */ @Cancelable public static class Pre extends Item { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Pre(GeoItemRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Pre( + GeoItemRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -531,13 +632,25 @@ public int getPackedLight() { * This event is called after {@link GeoRenderer#postRender} */ public static class Post extends Item { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Post(GeoItemRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Post( + GeoItemRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -574,6 +687,7 @@ public int getPackedLight() { * Use this event to add render layers to the renderer as needed */ public static class CompileRenderLayers extends Item { + public CompileRenderLayers(GeoItemRenderer renderer) { super(renderer); } @@ -581,7 +695,8 @@ public CompileRenderLayers(GeoItemRenderer renderer) { /** * Adds a {@link GeoRenderLayer} to the renderer *

- * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and renderer + * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and + * renderer */ public void addLayer(GeoRenderLayer renderLayer) { getRenderer().addRenderLayer(renderLayer); @@ -593,6 +708,7 @@ public void addLayer(GeoRenderLayer renderLayer) { * Renderer events for miscellaneous {@link GeoAnimatable animatables} being rendered by {@link GeoObjectRenderer} */ abstract class Object extends Event implements GeoRenderEvent { + private final GeoObjectRenderer renderer; public Object(GeoObjectRenderer renderer) { @@ -613,17 +729,30 @@ public GeoObjectRenderer getRenderer() { * This event is called before rendering, but after {@link GeoRenderer#preRender} *

* This event is Cancelable
- * If the event is cancelled, the object will not be rendered and the corresponding {@link Post} event will not be fired. + * If the event is cancelled, the object will not be rendered and the corresponding {@link Post} event will not + * be fired. */ @Cancelable public static class Pre extends Object { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Pre(GeoObjectRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Pre( + GeoObjectRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -656,17 +785,29 @@ public int getPackedLight() { /** * Post-render event for miscellaneous animatables being rendered by {@link GeoObjectRenderer} -

+ *

* This event is called after {@link GeoRenderer#postRender} */ public static class Post extends Object { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Post(GeoObjectRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Post( + GeoObjectRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -703,6 +844,7 @@ public int getPackedLight() { * Use this event to add render layers to the renderer as needed */ public static class CompileRenderLayers extends Object { + public CompileRenderLayers(GeoObjectRenderer renderer) { super(renderer); } @@ -710,7 +852,8 @@ public CompileRenderLayers(GeoObjectRenderer renderer) { /** * Adds a {@link GeoRenderLayer} to the renderer *

- * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and renderer + * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and + * renderer */ public void addLayer(GeoRenderLayer renderLayer) { getRenderer().addRenderLayer(renderLayer); @@ -719,9 +862,11 @@ public void addLayer(GeoRenderLayer renderLayer) { } /** - * Renderer events for miscellaneous {@link GeoReplacedEntity replaced entities} being rendered by {@link GeoReplacedEntityRenderer} + * Renderer events for miscellaneous {@link GeoReplacedEntity replaced entities} being rendered by + * {@link GeoReplacedEntityRenderer} */ abstract class ReplacedEntity extends Event implements GeoRenderEvent { + private final GeoReplacedEntityRenderer renderer; public ReplacedEntity(GeoReplacedEntityRenderer renderer) { @@ -749,17 +894,30 @@ public net.minecraft.world.entity.Entity getReplacedEntity() { * This event is called before rendering, but after {@link GeoRenderer#preRender} *

* This event is Cancelable
- * If the event is cancelled, the entity will not be rendered and the corresponding {@link Post} event will not be fired. + * If the event is cancelled, the entity will not be rendered and the corresponding {@link Post} event will not + * be fired. */ @Cancelable public static class Pre extends ReplacedEntity { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Pre(GeoReplacedEntityRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Pre( + GeoReplacedEntityRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -796,13 +954,25 @@ public int getPackedLight() { * This event is called after {@link GeoRenderer#postRender} */ public static class Post extends ReplacedEntity { + private final PoseStack poseStack; + private final BakedGeoModel model; + private final MultiBufferSource bufferSource; + private final float partialTick; + private final int packedLight; - public Post(GeoReplacedEntityRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { + public Post( + GeoReplacedEntityRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { super(renderer); this.poseStack = poseStack; @@ -839,6 +1009,7 @@ public int getPackedLight() { * Use this event to add render layers to the renderer as needed */ public static class CompileRenderLayers extends ReplacedEntity { + public CompileRenderLayers(GeoReplacedEntityRenderer renderer) { super(renderer); } @@ -846,11 +1017,12 @@ public CompileRenderLayers(GeoReplacedEntityRenderer renderer) { /** * Adds a {@link GeoRenderLayer} to the renderer *

- * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and renderer + * Type-safety is not checked here, so ensure that your layer is compatible with this animatable and + * renderer */ public void addLayer(GeoRenderLayer renderLayer) { getRenderer().addRenderLayer(renderLayer); } } } -} \ No newline at end of file +} diff --git a/neo/src/main/java/mod/azure/azurelib/items/NeoForgeAzureSpawnEgg.java b/neo/src/main/java/mod/azure/azurelib/items/NeoForgeAzureSpawnEgg.java new file mode 100644 index 000000000..6ee8ba6fc --- /dev/null +++ b/neo/src/main/java/mod/azure/azurelib/items/NeoForgeAzureSpawnEgg.java @@ -0,0 +1,24 @@ +package mod.azure.azurelib.items; + +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.item.Item; +import net.minecraftforge.common.ForgeSpawnEggItem; + +import java.util.function.Supplier; + +public class NeoForgeAzureSpawnEgg extends ForgeSpawnEggItem { + + public NeoForgeAzureSpawnEgg( + Supplier> type, + int primaryColor, + int secondaryColor + ) { + super(type, primaryColor, secondaryColor, new Item.Properties().stacksTo(64)); + } + + public NeoForgeAzureSpawnEgg(EntityType type, int primaryColor, int secondaryColor) { + super(() -> type, primaryColor, secondaryColor, new Item.Properties().stacksTo(64)); + } + +} diff --git a/neoforge/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java b/neo/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java similarity index 77% rename from neoforge/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java rename to neo/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java index 41fd08f6a..f11293751 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java +++ b/neo/src/main/java/mod/azure/azurelib/mixins/ClientHooksMixin.java @@ -1,8 +1,5 @@ package mod.azure.azurelib.mixins; -import mod.azure.azurelib.animatable.GeoItem; -import mod.azure.azurelib.animatable.client.RenderProvider; -import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.model.Model; import net.minecraft.world.entity.EquipmentSlot; @@ -14,15 +11,25 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import mod.azure.azurelib.animatable.GeoItem; +import mod.azure.azurelib.animatable.client.RenderProvider; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; + @Mixin(ForgeHooksClient.class) public class ClientHooksMixin { @Inject(method = "getArmorModel", at = @At("RETURN"), remap = false, cancellable = true) - private static void injectAzureArmors(LivingEntity entityLiving, ItemStack itemStack, EquipmentSlot slot, HumanoidModel _default, CallbackInfoReturnable cir) { + private static void injectAzureArmors( + LivingEntity entityLiving, + ItemStack itemStack, + EquipmentSlot slot, + HumanoidModel _default, + CallbackInfoReturnable cir + ) { if (itemStack.getItem() instanceof GeoItem) cir.setReturnValue( - RenderProvider.of(itemStack) - .getGenericArmorModel(entityLiving, itemStack, slot, (HumanoidModel) _default) + RenderProvider.of(itemStack) + .getGenericArmorModel(entityLiving, itemStack, slot, (HumanoidModel) _default) ); var renderer = AzArmorRendererRegistry.getOrNull(itemStack.getItem()); diff --git a/neoforge/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java b/neo/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java similarity index 79% rename from neoforge/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java rename to neo/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java index 311d9254b..88e8f41f8 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java +++ b/neo/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java @@ -4,10 +4,6 @@ import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalRef; import com.mojang.blaze3d.vertex.PoseStack; -import mod.azure.azurelib.animatable.GeoItem; -import mod.azure.azurelib.animatable.client.RenderProvider; -import mod.azure.azurelib.renderer.GeoArmorRenderer; -import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; @@ -20,6 +16,11 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import mod.azure.azurelib.animatable.GeoItem; +import mod.azure.azurelib.animatable.client.RenderProvider; +import mod.azure.azurelib.renderer.GeoArmorRenderer; +import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; + /** * Render hook for injecting AzureLib's armor rendering functionalities */ @@ -27,42 +28,42 @@ public class NeoMixinHumanoidArmorLayer> { @ModifyExpressionValue( - method = "renderArmorPiece", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/entity/LivingEntity;getItemBySlot(Lnet/minecraft/world/entity/EquipmentSlot;)Lnet/minecraft/world/item/ItemStack;" - ) + method = "renderArmorPiece", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;getItemBySlot(Lnet/minecraft/world/entity/EquipmentSlot;)Lnet/minecraft/world/item/ItemStack;" + ) ) private ItemStack azurelib$captureItemBySlot( - ItemStack original, - @Share("item_by_slot") LocalRef itemBySlotRef + ItemStack original, + @Share("item_by_slot") LocalRef itemBySlotRef ) { itemBySlotRef.set(original); return original; } @Inject( - method = "renderArmorPiece", at = @At( + method = "renderArmorPiece", at = @At( value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;renderModel(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/Model;ZFFFLnet/minecraft/resources/ResourceLocation;)V" - ), cancellable = true + ), cancellable = true ) public void azurelib$renderAzurelibModel( - PoseStack poseStack, - MultiBufferSource bufferSource, - T entity, - EquipmentSlot equipmentSlot, - int packedLight, - A baseModel, - CallbackInfo ci, - @Share("item_by_slot") LocalRef itemBySlotRef + PoseStack poseStack, + MultiBufferSource bufferSource, + T entity, + EquipmentSlot equipmentSlot, + int packedLight, + A baseModel, + CallbackInfo ci, + @Share("item_by_slot") LocalRef itemBySlotRef ) { var stack = itemBySlotRef.get(); var renderProvider = RenderProvider.of(stack); @SuppressWarnings("unchecked") var humanoidModel = (HumanoidModel) baseModel; var geckolibModel = renderProvider - .getGenericArmorModel(entity, stack, equipmentSlot, humanoidModel); + .getGenericArmorModel(entity, stack, equipmentSlot, humanoidModel); if (geckolibModel != null && stack.getItem() instanceof GeoItem) { if (geckolibModel instanceof GeoArmorRenderer geoArmorRenderer) { @@ -89,4 +90,4 @@ public class NeoMixinHumanoidArmorLayer NETWORK_VERSION) - .clientAcceptedVersions(NETWORK_VERSION::equals) - .serverAcceptedVersions(NETWORK_VERSION::equals) - .simpleChannel(); + .named(AzureLib.modResource("network_channel")) + .networkProtocolVersion(() -> NETWORK_VERSION) + .clientAcceptedVersions(NETWORK_VERSION::equals) + .serverAcceptedVersions(NETWORK_VERSION::equals) + .simpleChannel(); public static void sendClientPacket(ServerPlayer target, IPacket packet) { CHANNEL.sendTo(packet, target.connection.connection, NetworkDirection.PLAY_TO_CLIENT); @@ -40,7 +41,12 @@ private static

> void registerNetworkPacket(Class

packet try { packet = packetType.newInstance(); } catch (InstantiationException | IllegalAccessException e) { - throw new ReportedException(CrashReport.forThrowable(e, "Couldn't instantiate packet for registration. Make sure you have provided public constructor with no parameters.")); + throw new ReportedException( + CrashReport.forThrowable( + e, + "Couldn't instantiate packet for registration. Make sure you have provided public constructor with no parameters." + ) + ); } CHANNEL.registerMessage(packetIndex++, packetType, IPacket::encode, packet::decode, IPacket::handle); } diff --git a/neoforge/src/main/java/mod/azure/azurelib/network/S2C_SendConfigData.java b/neo/src/main/java/mod/azure/azurelib/network/S2C_SendConfigData.java similarity index 95% rename from neoforge/src/main/java/mod/azure/azurelib/network/S2C_SendConfigData.java rename to neo/src/main/java/mod/azure/azurelib/network/S2C_SendConfigData.java index 69a3b0f69..ada2e7b21 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/network/S2C_SendConfigData.java +++ b/neo/src/main/java/mod/azure/azurelib/network/S2C_SendConfigData.java @@ -1,16 +1,17 @@ package mod.azure.azurelib.network; -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.AzureLibException; -import mod.azure.azurelib.config.ConfigHolder; -import mod.azure.azurelib.config.adapter.TypeAdapter; -import mod.azure.azurelib.config.value.ConfigValue; import net.minecraft.network.FriendlyByteBuf; import net.minecraftforge.network.NetworkEvent; import java.util.Map; import java.util.function.Supplier; +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.AzureLibException; +import mod.azure.azurelib.config.ConfigHolder; +import mod.azure.azurelib.config.adapter.TypeAdapter; +import mod.azure.azurelib.config.value.ConfigValue; + public class S2C_SendConfigData implements IPacket { private final String config; @@ -49,7 +50,7 @@ public S2C_SendConfigData decode(FriendlyByteBuf buffer) { String fieldId = buffer.readUtf(); ConfigValue value = serialized.get(fieldId); if (value == null) { - AzureLib.LOGGER.fatal(Networking.MARKER, "Received unknown config value " + fieldId); + AzureLib.LOGGER.fatal(Networking.MARKER, "Received unknown config value " + fieldId); throw new AzureLibException("Unknown config field: " + fieldId); } setValue(value, buffer); diff --git a/neo/src/main/java/mod/azure/azurelib/platform/AzureLibEventsNeoForge.java b/neo/src/main/java/mod/azure/azurelib/platform/AzureLibEventsNeoForge.java new file mode 100644 index 000000000..3babc54cf --- /dev/null +++ b/neo/src/main/java/mod/azure/azurelib/platform/AzureLibEventsNeoForge.java @@ -0,0 +1,265 @@ +package mod.azure.azurelib.platform; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraftforge.common.MinecraftForge; + +import mod.azure.azurelib.cache.object.BakedGeoModel; +import mod.azure.azurelib.event.GeoRenderEvent; +import mod.azure.azurelib.platform.services.AzureEvents; +import mod.azure.azurelib.renderer.*; + +public class AzureLibEventsNeoForge implements AzureEvents { + + /** + * Fire the {@link GeoRenderEvent.Block.CompileRenderLayers} event + */ + @Override + public void fireCompileBlockRenderLayers(GeoBlockRenderer renderer) { + MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Block.CompileRenderLayers(renderer)); + } + + /** + * Fire the {@link GeoRenderEvent.Block.Pre} event + */ + @Override + public boolean fireBlockPreRender( + GeoBlockRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + return !MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Block.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.Block.Post} event + */ + @Override + public void fireBlockPostRender( + GeoBlockRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Block.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.Armor.CompileRenderLayers} event + */ + @Override + public void fireCompileArmorRenderLayers(GeoArmorRenderer renderer) { + MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Armor.CompileRenderLayers(renderer)); + } + + /** + * Fire the {@link GeoRenderEvent.Armor.Pre} event + */ + @Override + public boolean fireArmorPreRender( + GeoArmorRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + return !MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Armor.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.Armor.Post} event + */ + @Override + public void fireArmorPostRender( + GeoArmorRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Armor.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.Entity.CompileRenderLayers} event + */ + @Override + public void fireCompileEntityRenderLayers(GeoEntityRenderer renderer) { + MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Entity.CompileRenderLayers(renderer)); + } + + /** + * Fire the {@link GeoRenderEvent.Entity.Pre} event + */ + @Override + public boolean fireEntityPreRender( + GeoEntityRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + return !MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Entity.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.Entity.Post} event + */ + @Override + public void fireEntityPostRender( + GeoEntityRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Entity.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.ReplacedEntity.CompileRenderLayers} event + */ + @Override + public void fireCompileReplacedEntityRenderLayers(GeoReplacedEntityRenderer renderer) { + MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.ReplacedEntity.CompileRenderLayers(renderer)); + } + + /** + * Fire the {@link GeoRenderEvent.ReplacedEntity.Pre} event + */ + @Override + public boolean fireReplacedEntityPreRender( + GeoReplacedEntityRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + return !MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.ReplacedEntity.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.ReplacedEntity.Post} event + */ + @Override + public void fireReplacedEntityPostRender( + GeoReplacedEntityRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.ReplacedEntity.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.Item.CompileRenderLayers} event + */ + @Override + public void fireCompileItemRenderLayers(GeoItemRenderer renderer) { + MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Item.CompileRenderLayers(renderer)); + } + + /** + * Fire the {@link GeoRenderEvent.Item.Pre} event + */ + @Override + public boolean fireItemPreRender( + GeoItemRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + return !MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Item.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.Item.Post} event + */ + @Override + public void fireItemPostRender( + GeoItemRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Item.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.Object.CompileRenderLayers} event + */ + @Override + public void fireCompileObjectRenderLayers(GeoObjectRenderer renderer) { + MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Object.CompileRenderLayers(renderer)); + } + + /** + * Fire the {@link GeoRenderEvent.Object.Pre} event + */ + @Override + public boolean fireObjectPreRender( + GeoObjectRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + return !MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Object.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } + + /** + * Fire the {@link GeoRenderEvent.Object.Post} event + */ + @Override + public void fireObjectPostRender( + GeoObjectRenderer renderer, + PoseStack poseStack, + BakedGeoModel model, + MultiBufferSource bufferSource, + float partialTick, + int packedLight + ) { + MinecraftForge.EVENT_BUS.post( + new GeoRenderEvent.Object.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight) + ); + } +} diff --git a/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibInitializer.java b/neo/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibInitializer.java similarity index 92% rename from neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibInitializer.java rename to neo/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibInitializer.java index 78512041e..3c5475913 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibInitializer.java +++ b/neo/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibInitializer.java @@ -1,12 +1,13 @@ package mod.azure.azurelib.platform; -import mod.azure.azurelib.cache.AzureLibCache; -import mod.azure.azurelib.platform.services.AzureLibInitializer; import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.loading.FMLEnvironment; +import mod.azure.azurelib.cache.AzureLibCache; +import mod.azure.azurelib.platform.services.AzureLibInitializer; + public class NeoForgeAzureLibInitializer implements AzureLibInitializer { + @Override public void initialize() { if (FMLEnvironment.dist == Dist.CLIENT) { @@ -14,4 +15,4 @@ public void initialize() { } Services.NETWORK.registerClientReceiverPackets(); } -} \ No newline at end of file +} diff --git a/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java b/neo/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java similarity index 50% rename from neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java rename to neo/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java index 45673a397..f5db032d9 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java +++ b/neo/src/main/java/mod/azure/azurelib/platform/NeoForgeAzureLibNetwork.java @@ -1,15 +1,8 @@ package mod.azure.azurelib.platform; -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.network.AbstractPacket; -import mod.azure.azurelib.network.Networking; -import mod.azure.azurelib.network.S2C_SendConfigData; -import mod.azure.azurelib.network.packet.*; -import mod.azure.azurelib.platform.services.AzureLibNetwork; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; @@ -21,9 +14,23 @@ import java.util.function.Supplier; +import mod.azure.azurelib.AzureLib; +import mod.azure.azurelib.network.AbstractPacket; +import mod.azure.azurelib.network.Networking; +import mod.azure.azurelib.network.S2C_SendConfigData; +import mod.azure.azurelib.network.packet.*; +import mod.azure.azurelib.platform.services.AzureLibNetwork; + public class NeoForgeAzureLibNetwork implements AzureLibNetwork { + private static final String VER = "1"; - private static final SimpleChannel PACKET_CHANNEL = NetworkRegistry.newSimpleChannel(AzureLib.modResource("main"), () -> VER, VER::equals, VER::equals); + + private static final SimpleChannel PACKET_CHANNEL = NetworkRegistry.newSimpleChannel( + AzureLib.modResource("main"), + () -> VER, + VER::equals, + VER::equals + ); @Override public Packet createPacket(Entity entity) { @@ -40,16 +47,70 @@ private void handlePacket(AbstractPacket packet, Supplier public void registerClientReceiverPackets() { int id = 0; - PACKET_CHANNEL.registerMessage(id++, AnimDataSyncPacket.class, AnimDataSyncPacket::encode, AnimDataSyncPacket::receive, this::handlePacket); - PACKET_CHANNEL.registerMessage(id++, AnimTriggerPacket.class, AnimTriggerPacket::encode, AnimTriggerPacket::receive, this::handlePacket); - PACKET_CHANNEL.registerMessage(id++, EntityAnimDataSyncPacket.class, EntityAnimDataSyncPacket::encode, EntityAnimDataSyncPacket::receive, this::handlePacket); - PACKET_CHANNEL.registerMessage(id++, EntityAnimTriggerPacket.class, EntityAnimTriggerPacket::encode, EntityAnimTriggerPacket::receive, this::handlePacket); - PACKET_CHANNEL.registerMessage(id++, BlockEntityAnimDataSyncPacket.class, BlockEntityAnimDataSyncPacket::encode, BlockEntityAnimDataSyncPacket::receive, this::handlePacket); - PACKET_CHANNEL.registerMessage(id++, BlockEntityAnimTriggerPacket.class, BlockEntityAnimTriggerPacket::encode, BlockEntityAnimTriggerPacket::receive, this::handlePacket); + PACKET_CHANNEL.registerMessage( + id++, + AnimDataSyncPacket.class, + AnimDataSyncPacket::encode, + AnimDataSyncPacket::receive, + this::handlePacket + ); + PACKET_CHANNEL.registerMessage( + id++, + AnimTriggerPacket.class, + AnimTriggerPacket::encode, + AnimTriggerPacket::receive, + this::handlePacket + ); + PACKET_CHANNEL.registerMessage( + id++, + EntityAnimDataSyncPacket.class, + EntityAnimDataSyncPacket::encode, + EntityAnimDataSyncPacket::receive, + this::handlePacket + ); + PACKET_CHANNEL.registerMessage( + id++, + EntityAnimTriggerPacket.class, + EntityAnimTriggerPacket::encode, + EntityAnimTriggerPacket::receive, + this::handlePacket + ); + PACKET_CHANNEL.registerMessage( + id++, + BlockEntityAnimDataSyncPacket.class, + BlockEntityAnimDataSyncPacket::encode, + BlockEntityAnimDataSyncPacket::receive, + this::handlePacket + ); + PACKET_CHANNEL.registerMessage( + id++, + BlockEntityAnimTriggerPacket.class, + BlockEntityAnimTriggerPacket::encode, + BlockEntityAnimTriggerPacket::receive, + this::handlePacket + ); - PACKET_CHANNEL.registerMessage(id++, AzBlockEntityDispatchCommandPacket.class, AzBlockEntityDispatchCommandPacket::encode, AzBlockEntityDispatchCommandPacket::receive, this::handlePacket); - PACKET_CHANNEL.registerMessage(id++, AzEntityDispatchCommandPacket.class, AzEntityDispatchCommandPacket::encode, AzEntityDispatchCommandPacket::receive, this::handlePacket); - PACKET_CHANNEL.registerMessage(id++, AzItemStackDispatchCommandPacket.class, AzItemStackDispatchCommandPacket::encode, AzItemStackDispatchCommandPacket::receive, this::handlePacket); + PACKET_CHANNEL.registerMessage( + id++, + AzBlockEntityDispatchCommandPacket.class, + AzBlockEntityDispatchCommandPacket::encode, + AzBlockEntityDispatchCommandPacket::receive, + this::handlePacket + ); + PACKET_CHANNEL.registerMessage( + id++, + AzEntityDispatchCommandPacket.class, + AzEntityDispatchCommandPacket::encode, + AzEntityDispatchCommandPacket::receive, + this::handlePacket + ); + PACKET_CHANNEL.registerMessage( + id++, + AzItemStackDispatchCommandPacket.class, + AzItemStackDispatchCommandPacket::encode, + AzItemStackDispatchCommandPacket::receive, + this::handlePacket + ); } @Override diff --git a/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgePlatformHelper.java b/neo/src/main/java/mod/azure/azurelib/platform/NeoForgePlatformHelper.java similarity index 99% rename from neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgePlatformHelper.java rename to neo/src/main/java/mod/azure/azurelib/platform/NeoForgePlatformHelper.java index 0023c1284..a75197ab1 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/platform/NeoForgePlatformHelper.java +++ b/neo/src/main/java/mod/azure/azurelib/platform/NeoForgePlatformHelper.java @@ -1,9 +1,5 @@ package mod.azure.azurelib.platform; -import mod.azure.azurelib.NeoForgeAzureLibMod; -import mod.azure.azurelib.entities.TickingLightBlock; -import mod.azure.azurelib.entities.TickingLightEntity; -import mod.azure.azurelib.platform.services.IPlatformHelper; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraftforge.fml.ModList; @@ -13,23 +9,25 @@ import java.nio.file.Path; +import mod.azure.azurelib.NeoForgeAzureLibMod; +import mod.azure.azurelib.entities.TickingLightBlock; +import mod.azure.azurelib.entities.TickingLightEntity; +import mod.azure.azurelib.platform.services.IPlatformHelper; + public class NeoForgePlatformHelper implements IPlatformHelper { @Override public String getPlatformName() { - return "Forge"; } @Override public boolean isModLoaded(String modId) { - return ModList.get().isLoaded(modId); } @Override public boolean isDevelopmentEnvironment() { - return !FMLLoader.isProduction(); } @@ -62,4 +60,4 @@ public Enchantment getIncendairyenchament() { public Path modsDir() { return FMLPaths.MODSDIR.get(); } -} \ No newline at end of file +} diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java b/neo/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java similarity index 92% rename from neoforge/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java rename to neo/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java index e8444277c..621768110 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java +++ b/neo/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java @@ -1,7 +1,5 @@ package mod.azure.azurelib.testing.block; -import mod.azure.azurelib.NeoForgeAzureLibMod; -import mod.azure.azurelib.testing.block.be.StargateBlockEntity; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; @@ -13,6 +11,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import mod.azure.azurelib.NeoForgeAzureLibMod; +import mod.azure.azurelib.testing.block.be.StargateBlockEntity; + public class StargateBlock extends BaseEntityBlock { public StargateBlock() { @@ -47,6 +48,10 @@ public BlockEntityTicker getTicker( @NotNull BlockState state, @NotNull BlockEntityType type ) { - return createTickerHelper(type, NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), StargateBlockEntity::tick); + return createTickerHelper( + type, + NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), + StargateBlockEntity::tick + ); } } diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java similarity index 100% rename from neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java rename to neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java similarity index 99% rename from neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java rename to neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java index 886fb372a..505f0172f 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java +++ b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java @@ -1,11 +1,12 @@ package mod.azure.azurelib.testing.block.be; -import mod.azure.azurelib.NeoForgeAzureLibMod; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import mod.azure.azurelib.NeoForgeAzureLibMod; + public class StargateBlockEntity extends BlockEntity { public final StargateBlockAnimationDispatcher animationDispatcher; diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java similarity index 95% rename from neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java rename to neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java index 17ef49324..a7b639726 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java +++ b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java @@ -1,13 +1,13 @@ package mod.azure.azurelib.testing.block.be; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + import mod.azure.azurelib.AzureLib; import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; import mod.azure.azurelib.rewrite.animation.impl.AzBlockAnimator; -import mod.azure.azurelib.testing.block.be.StargateBlockEntity; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; /** * StargateBlockEntityAnimator is responsible for managing and configuring animations for the StargateBlockEntity. It diff --git a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java similarity index 99% rename from neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java rename to neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java index 49be22d61..efe46da18 100644 --- a/neoforge/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java +++ b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java @@ -1,9 +1,10 @@ package mod.azure.azurelib.testing.block.be; +import net.minecraft.resources.ResourceLocation; + import mod.azure.azurelib.AzureLib; import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRenderer; import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRendererConfig; -import net.minecraft.resources.ResourceLocation; public class StargateBlockRenderer extends AzBlockEntityRenderer { diff --git a/neoforge/src/main/resources/META-INF/mods.toml b/neo/src/main/resources/META-INF/mods.toml similarity index 100% rename from neoforge/src/main/resources/META-INF/mods.toml rename to neo/src/main/resources/META-INF/mods.toml diff --git a/neoforge/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureEvents b/neo/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureEvents similarity index 100% rename from neoforge/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureEvents rename to neo/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureEvents diff --git a/neoforge/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibInitializer b/neo/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibInitializer similarity index 100% rename from neoforge/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibInitializer rename to neo/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibInitializer diff --git a/neoforge/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibNetwork b/neo/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibNetwork similarity index 100% rename from neoforge/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibNetwork rename to neo/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.AzureLibNetwork diff --git a/neoforge/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.IPlatformHelper b/neo/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.IPlatformHelper similarity index 100% rename from neoforge/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.IPlatformHelper rename to neo/src/main/resources/META-INF/services/mod.azure.azurelib.platform.services.IPlatformHelper diff --git a/neoforge/src/main/resources/azurelib.forge.mixins.json b/neo/src/main/resources/azurelib.forge.mixins.json similarity index 100% rename from neoforge/src/main/resources/azurelib.forge.mixins.json rename to neo/src/main/resources/azurelib.forge.mixins.json diff --git a/neoforge/build.gradle b/neoforge/build.gradle deleted file mode 100644 index 631131bad..000000000 --- a/neoforge/build.gradle +++ /dev/null @@ -1,170 +0,0 @@ -plugins { - id 'idea' - id 'maven-publish' - id 'net.minecraftforge.gradle' - id 'org.spongepowered.mixin' - id "me.modmuss50.mod-publish-plugin" version "0.8.4" -} - -base { - archivesName = "${mod_id}-neo-${minecraft_version}" -} - -mixin { - add(sourceSets.main, "${mod_id}.refmap.json") - - config("${mod_id}.mixins.json") - config("${mod_id}.forge.mixins.json") -} - -minecraft { - mappings channel: 'official', version: minecraft_version - - copyIdeResources = true //Calls processResources when in dev - - if (file('src/main/resources/META-INF/accesstransformer.cfg').exists()) { - accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') - } - - runs { - client { - workingDirectory project.file('run') - ideaModule "${rootProject.name}.${project.name}.main" - taskName 'Client' - property 'mixin.env.remapRefMap', 'true' - property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" - mods { - modClientRun { - source sourceSets.main - source project(":common").sourceSets.main - } - } - } - - server { - workingDirectory project.file('run') - ideaModule "${rootProject.name}.${project.name}.main" - taskName 'Server' - property 'mixin.env.remapRefMap', 'true' - property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" - mods { - modServerRun { - source sourceSets.main - source project(":common").sourceSets.main - } - } - } - - data { - workingDirectory project.file('run') - ideaModule "${rootProject.name}.${project.name}.main" - args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') - taskName 'Data' - property 'mixin.env.remapRefMap', 'true' - property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" - mods { - modDataRun { - source sourceSets.main - source project(":common").sourceSets.main - } - } - } - } -} - -sourceSets.main.resources.srcDir 'src/generated/resources' - -repositories { - maven { url "https://cfa2.cursemaven.com" } - mavenCentral() -} - -dependencies { - minecraft "net.minecraftforge:forge:${minecraft_version}-${neo_version}" - compileOnly project(":common") - annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT:processor") - compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")) - implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.4.1")) { - jarJar.ranged(it, "[0.4.1,)") - } -} - -tasks.withType(JavaCompile).configureEach { - source(project(":common").sourceSets.main.allSource) -} -tasks.withType(Javadoc).configureEach { - source(project(":common").sourceSets.main.allJava) -} -tasks.named("sourcesJar", Jar) { - from(project(":common").sourceSets.main.allSource) -} - -processResources { - from project(":common").sourceSets.main.resources -} - -jar.finalizedBy('reobfJar') - -publishing { - repositories { - maven { - name = "azurelib" - url = "https://maven.azuredoom.com/mods" - credentials(PasswordCredentials) - authentication { - basic(BasicAuthentication) - } - } - } - publications { - maven(MavenPublication) { - artifactId base.archivesName.get() - artifact jar - artifact sourcesJar - pom.withXml { - asNode().dependencies.dependency.each { dep -> - if (dep.groupId.text() == 'net.minecraftforge' && dep.artifactId.text() == 'forge') { - dep.parent().remove(dep) - } - } - } - } - } -} - -if (file('key.properties').exists()) { - publishMods { - def releaseProp = new Properties() - File secretPropsFile = file("key.properties") - releaseProp.load(secretPropsFile.newInputStream()) - file = jar.archiveFile - changelog = rootProject.file("changelog.md").text - type = STABLE - modLoaders.add("neoforge") - modLoaders.add("forge") - - discord { - webhookUrl = releaseProp.getProperty("discordKey") - dryRunWebhookUrl = releaseProp.getProperty("discordKey") - username = "AzureBot" - avatarUrl = "https://cdn.modrinth.com/data/7zlUOZvb/8b2cb16452c1d3bb33574519a6f5cf4ce86f13a3_96.webp" - content = changelog.map { "# A new version of AzureLib for Forge has been released for 1.20.1 \n" + it} - setPlatforms(publishMods.platforms.curseforge, publishMods.platforms.modrinth) - style { - look = "MODERN" - thumbnailUrl = "https://cdn.modrinth.com/data/7zlUOZvb/8b2cb16452c1d3bb33574519a6f5cf4ce86f13a3_96.webp" - } - } - curseforge { - projectId = "817423" - projectSlug = "azurelib" - accessToken = releaseProp.getProperty("curseKey") - minecraftVersions.add(project.minecraft_version) - } - modrinth { - projectId = "7zlUOZvb" - accessToken = releaseProp.getProperty('modrinthKey') - minecraftVersions.add(project.minecraft_version) - } - } -} \ No newline at end of file diff --git a/neoforge/changelog.txt b/neoforge/changelog.txt deleted file mode 100644 index e5969dbe6..000000000 --- a/neoforge/changelog.txt +++ /dev/null @@ -1,3 +0,0 @@ -v1.0.34 - -- Adds Missing EntityPacket \ No newline at end of file diff --git a/neoforge/src/main/java/mod/azure/azurelib/items/NeoForgeAzureSpawnEgg.java b/neoforge/src/main/java/mod/azure/azurelib/items/NeoForgeAzureSpawnEgg.java deleted file mode 100644 index 7ea6912e2..000000000 --- a/neoforge/src/main/java/mod/azure/azurelib/items/NeoForgeAzureSpawnEgg.java +++ /dev/null @@ -1,20 +0,0 @@ -package mod.azure.azurelib.items; - -import java.util.function.Supplier; - -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.item.Item; -import net.minecraftforge.common.ForgeSpawnEggItem; - -public class NeoForgeAzureSpawnEgg extends ForgeSpawnEggItem { - - public NeoForgeAzureSpawnEgg(Supplier> type, int primaryColor, int secondaryColor) { - super(type, primaryColor, secondaryColor, new Item.Properties().stacksTo(64)); - } - - public NeoForgeAzureSpawnEgg(EntityType type, int primaryColor, int secondaryColor) { - super(() -> type, primaryColor, secondaryColor, new Item.Properties().stacksTo(64)); - } - -} \ No newline at end of file diff --git a/neoforge/src/main/java/mod/azure/azurelib/platform/AzureLibEventsNeoForge.java b/neoforge/src/main/java/mod/azure/azurelib/platform/AzureLibEventsNeoForge.java deleted file mode 100644 index 952f33f39..000000000 --- a/neoforge/src/main/java/mod/azure/azurelib/platform/AzureLibEventsNeoForge.java +++ /dev/null @@ -1,155 +0,0 @@ -package mod.azure.azurelib.platform; - -import com.mojang.blaze3d.vertex.PoseStack; -import mod.azure.azurelib.cache.object.BakedGeoModel; -import mod.azure.azurelib.event.GeoRenderEvent; -import mod.azure.azurelib.platform.services.AzureEvents; -import mod.azure.azurelib.renderer.*; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraftforge.common.MinecraftForge; - -public class AzureLibEventsNeoForge implements AzureEvents { - /** - * Fire the {@link GeoRenderEvent.Block.CompileRenderLayers} event - */ - @Override - public void fireCompileBlockRenderLayers(GeoBlockRenderer renderer) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Block.CompileRenderLayers(renderer)); - } - - /** - * Fire the {@link GeoRenderEvent.Block.Pre} event - */ - @Override - public boolean fireBlockPreRender(GeoBlockRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - return !MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Block.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.Block.Post} event - */ - @Override - public void fireBlockPostRender(GeoBlockRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Block.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.Armor.CompileRenderLayers} event - */ - @Override - public void fireCompileArmorRenderLayers(GeoArmorRenderer renderer) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Armor.CompileRenderLayers(renderer)); - } - - /** - * Fire the {@link GeoRenderEvent.Armor.Pre} event - */ - @Override - public boolean fireArmorPreRender(GeoArmorRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - return !MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Armor.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.Armor.Post} event - */ - @Override - public void fireArmorPostRender(GeoArmorRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Armor.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.Entity.CompileRenderLayers} event - */ - @Override - public void fireCompileEntityRenderLayers(GeoEntityRenderer renderer) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Entity.CompileRenderLayers(renderer)); - } - - /** - * Fire the {@link GeoRenderEvent.Entity.Pre} event - */ - @Override - public boolean fireEntityPreRender(GeoEntityRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - return !MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Entity.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.Entity.Post} event - */ - @Override - public void fireEntityPostRender(GeoEntityRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Entity.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.ReplacedEntity.CompileRenderLayers} event - */ - @Override - public void fireCompileReplacedEntityRenderLayers(GeoReplacedEntityRenderer renderer) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.ReplacedEntity.CompileRenderLayers(renderer)); - } - - /** - * Fire the {@link GeoRenderEvent.ReplacedEntity.Pre} event - */ - @Override - public boolean fireReplacedEntityPreRender(GeoReplacedEntityRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - return !MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.ReplacedEntity.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.ReplacedEntity.Post} event - */ - @Override - public void fireReplacedEntityPostRender(GeoReplacedEntityRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.ReplacedEntity.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.Item.CompileRenderLayers} event - */ - @Override - public void fireCompileItemRenderLayers(GeoItemRenderer renderer) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Item.CompileRenderLayers(renderer)); - } - - /** - * Fire the {@link GeoRenderEvent.Item.Pre} event - */ - @Override - public boolean fireItemPreRender(GeoItemRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - return !MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Item.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.Item.Post} event - */ - @Override - public void fireItemPostRender(GeoItemRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Item.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.Object.CompileRenderLayers} event - */ - @Override - public void fireCompileObjectRenderLayers(GeoObjectRenderer renderer) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Object.CompileRenderLayers(renderer)); - } - - /** - * Fire the {@link GeoRenderEvent.Object.Pre} event - */ - @Override - public boolean fireObjectPreRender(GeoObjectRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - return !MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Object.Pre(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } - - /** - * Fire the {@link GeoRenderEvent.Object.Post} event - */ - @Override - public void fireObjectPostRender(GeoObjectRenderer renderer, PoseStack poseStack, BakedGeoModel model, MultiBufferSource bufferSource, float partialTick, int packedLight) { - MinecraftForge.EVENT_BUS.post(new GeoRenderEvent.Object.Post(renderer, poseStack, model, bufferSource, partialTick, packedLight)); - } -} diff --git a/neoforge/src/main/resources/META-INF/accesstransformer.cfg b/neoforge/src/main/resources/META-INF/accesstransformer.cfg deleted file mode 100644 index 6ebcd7064..000000000 --- a/neoforge/src/main/resources/META-INF/accesstransformer.cfg +++ /dev/null @@ -1,15 +0,0 @@ -public net.minecraft.client.model.geom.ModelPart$Cube -public net.minecraft.client.model.geom.ModelPart f_104212_ # cubes -public net.minecraft.world.entity.Entity m_7939_()Lnet/minecraft/world/phys/Vec3; # getLeashOffset -public net.minecraft.world.entity.WalkAnimationState f_267406_ # speedOld - -public net.minecraft.client.model.AgeableListModel f_102007_ # scaleHead -public net.minecraft.client.model.AgeableListModel f_170338_ # babyYHeadOffset -public net.minecraft.client.model.AgeableListModel f_170339_ # babyZHeadOffset -public net.minecraft.client.model.AgeableListModel f_102010_ # babyHeadScale -public net.minecraft.client.model.AgeableListModel f_102011_ # babyBodyScale -public net.minecraft.client.model.AgeableListModel f_102012_ # bodyYOffset - -public-f net.minecraft.client.renderer.LevelRenderer f_109464_ # renderBuffers -public-f net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer m_289609_(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/HumanoidModel;ZFFFLjava/lang/String;)V # renderModel -public com.mojang.blaze3d.vertex.BufferBuilder f_85661_ # building \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index bfbf75323..27dfb0b6c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,5 +24,5 @@ plugins { rootProject.name = 'AzureLib 1.20.1' include("common") include("fabric") -include("neoforge") +include("neo") From 5c3762c3d421a8e560c0fab1d25c50a9ccc39d55 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 18:06:28 -0400 Subject: [PATCH 22/40] Fixes Java doc --- .../impl/root/AzRootSetTransitionSpeedAction.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java index 1ce7c1624..98c245e63 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java @@ -11,11 +11,12 @@ import java.util.function.Function; /** - * The {@code AzRootSetTransitionSpeedAction} class implements the {@link AzAction} interface and represents an action - * that modifies the transition speed for an animator during an animation state. This action is intended for use within - * the animation system to adjust the transition timing of animations. This class provides a unique resource location - * identifier for this specific action and handles the logic required to apply the transition speed modification to the - * target {@link AzAnimator}. It utilizes {@link StreamCodec} for serialization and deserialization of this action. + * Represents an action that sets the transition speed of animation controllers within an {@link AzAnimator}. + * This action is part of the AzureLib animation system and provides a means to modify the transition length + * property of all animation controllers contained in the target animator. + * + * The {@link AzRootSetTransitionSpeedAction} encapsulates a single `transitionSpeed` value, which determines + * the length of animation transition in seconds when applied during animation state changes. */ public record AzRootSetTransitionSpeedAction( float transitionSpeed From 5995a380c4d9ce9b217dddf4d095c68d22a86e62 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 18:07:00 -0400 Subject: [PATCH 23/40] Update gradle.properties --- gradle.properties | 54 ++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/gradle.properties b/gradle.properties index 19327fa1c..18a2d4a1a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,37 +4,43 @@ # Gradle org.gradle.jvmargs = -Xmx3G +org.gradle.daemon = false # Project -group = mod.azure.azurelib +group = mod.azure.azurelib +modmuss50_mod_publish_version = 0.8.4 +parchment_minecraft = 1.20.1 +parchment_version = 2023.09.03 +java_version = 17 # Common -minecraft_version = 1.20.1 -archives_base_name = azurelib-1.20.1 +minecraft_version = 1.20.1 +archives_base_name = azurelib-1.20.1 # Forge -neo_version = 47.4.0 -loader_version_range = [47,) -neo_version_range = [47.1,) -minecraft_version_range = [1.20.1, 1.20.2) -mapping_channel = official -mapping_version = 1.20.1 +neo_version = 47.4.0 +loader_version_range = [47,) +neo_version_range = [47.1,) +minecraft_version_range = [1.20.1, 1.20.2) +mapping_channel = official +mapping_version = 1.20.1 # Fabric -fabric_version = 0.83.0+1.20.1 -fabric_loader_version = 0.14.25 -modmenu_version = 7.2.2 +fabric_version = 0.83.0+1.20.1 +fabric_loader_version = 0.14.25 +modmenu_version = 7.2.2 # Mod options -maven_group = mod.azure.azurelib -mod_name = AzureLib -mod_author = AzureDoom -version = 2.0.41 -mod_id = azurelib -mod_license = MIT -mod_description = Based off Geckolib but now just for my own needs. -mod_credits = AzureDoom, Gecko, Eliot, Chappie, DerToaster, Tslat, Bvanseg -mod_logo = azurelib.png -mod_url = https://modrinth.com/mod/azurelib -mod_issues = https://github.com/AzureDoom/AzureLib/issues -mod_sources = https://github.com/AzureDoom/AzureLib \ No newline at end of file +maven_group = mod.azure.azurelib +mod_name = AzureLib +mod_author = AzureDoom +version = 2.0.41 +mod_id = azurelib +maven_url = https://maven.azuredoom.com/mods +mod_license = MIT +mod_description = Based off Geckolib but now just for my own needs. +mod_credits = AzureDoom, Gecko, Eliot, Chappie, DerToaster, Tslat, Bvanseg +mod_logo = azurelib.png +mod_url = https://modrinth.com/mod/azurelib +mod_issues = https://github.com/AzureDoom/AzureLib/issues +mod_sources = https://github.com/AzureDoom/AzureLib \ No newline at end of file From d02ff63a77b2ea588724ca0ff0e0374e711f654f Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 18:22:23 -0400 Subject: [PATCH 24/40] Update NeoForgeAzureLibMod.java --- neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java | 1 + 1 file changed, 1 insertion(+) diff --git a/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java b/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java index b8a71ea48..90e4c926e 100644 --- a/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java +++ b/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java @@ -48,6 +48,7 @@ public NeoForgeAzureLibMod() { AzureEnchantments.ENCHANTMENTS.register(modEventBus); AzureBlocks.BLOCKS.register(modEventBus); AzureEntities.TILE_TYPES.register(modEventBus); + AzureEntities.ENTITY_TYPES.register(modEventBus); AzureItems.ITEMS.register(modEventBus); modEventBus.addListener(this::createEntityAttributes); modEventBus.addListener(this::commonSetup); From d2a50152881de3c3bcb3344df9f7ad3128d6b5f5 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 22:07:22 -0400 Subject: [PATCH 25/40] Fixes Item IDs not applying on Forge. --- ...bstractContainerMenuMixin_AzItemIDFix.java | 31 +++++++++++++------ .../resources/azurelib.fabric.mixins.json | 1 + .../main/resources/azurelib.forge.mixins.json | 6 ++-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java b/common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java index 350c99aea..06012c8bb 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java @@ -1,11 +1,15 @@ package mod.azure.azurelib.mixins; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import mod.azure.azurelib.AzureLib; import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; 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.Redirect; @@ -18,7 +22,9 @@ * AzureLib-registered item stacks with custom identifiers during container interactions. */ @Mixin(AbstractContainerMenu.class) -public class AbstractContainerMenuMixin_AzItemIDFix { +public abstract class AbstractContainerMenuMixin_AzItemIDFix { + + @Shadow public abstract void removed(Player player); @Unique private static final int DEFAULT_AZ_ID = -1; @@ -54,7 +60,7 @@ public class AbstractContainerMenuMixin_AzItemIDFix { *

* Tooltip: Compares two `ItemStack` objects, ensuring their Az IDs (if present) also match. */ - @Redirect( + @WrapOperation( method = "synchronizeSlotToRemote", at = @At( value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;matches(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z" @@ -62,9 +68,10 @@ public class AbstractContainerMenuMixin_AzItemIDFix { ) public boolean azurelib$syncAzureIDWithRemote( ItemStack itemStack, - ItemStack comparisonItemStack + ItemStack comparisonItemStack, + Operation original ) { - return azurelib$compareStacksWithAzureID(itemStack, comparisonItemStack); + return azurelib$compareStacksWithAzureID(itemStack, comparisonItemStack, original); } /** @@ -73,7 +80,7 @@ public class AbstractContainerMenuMixin_AzItemIDFix { * Tooltip: Ensures that slot listeners detect changes in AzureLib-registered item stacks based not only on their * normal properties but also their Az ID values, if applicable. */ - @Redirect( + @WrapOperation( method = "triggerSlotListeners", at = @At( value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;matches(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z" @@ -81,9 +88,10 @@ public class AbstractContainerMenuMixin_AzItemIDFix { ) public boolean azurelib$detectSlotChangeWithAzureID( ItemStack itemStack, - ItemStack comparisonItemStack + ItemStack comparisonItemStack, + Operation original ) { - return azurelib$compareStacksWithAzureID(itemStack, comparisonItemStack); + return azurelib$compareStacksWithAzureID(itemStack, comparisonItemStack, original); } /** @@ -94,9 +102,12 @@ public class AbstractContainerMenuMixin_AzItemIDFix { * @return True if the base comparison is true and the Az IDs (if present) match; false otherwise. */ @Unique - private boolean azurelib$compareStacksWithAzureID(ItemStack itemStack, ItemStack comparisonItemStack) { - return ItemStack.matches(itemStack, comparisonItemStack) && - (!AzIdentityRegistry.hasIdentity(itemStack.getItem()) || azurelib$checkAzIDMatch(itemStack.getTag(), comparisonItemStack.getTag())); + private boolean azurelib$compareStacksWithAzureID(ItemStack itemStack, ItemStack comparisonItemStack, Operation original) { + if (!AzIdentityRegistry.hasIdentity(itemStack.getItem())) { + return original.call(itemStack, comparisonItemStack); + } + + return original.call(itemStack, comparisonItemStack) && azurelib$checkAzIDMatch(itemStack.getTag(), comparisonItemStack.getTag()); } /** diff --git a/fabric/src/main/resources/azurelib.fabric.mixins.json b/fabric/src/main/resources/azurelib.fabric.mixins.json index 2e48d5105..161f22e08 100644 --- a/fabric/src/main/resources/azurelib.fabric.mixins.json +++ b/fabric/src/main/resources/azurelib.fabric.mixins.json @@ -2,6 +2,7 @@ "required": true, "package": "mod.azure.azurelib.mixins", "compatibilityLevel": "JAVA_17", + "refmap": "azurelib.refmap.json", "injectors": { "defaultRequire": 1 }, diff --git a/neo/src/main/resources/azurelib.forge.mixins.json b/neo/src/main/resources/azurelib.forge.mixins.json index 0471c8e1b..2ca5e974f 100644 --- a/neo/src/main/resources/azurelib.forge.mixins.json +++ b/neo/src/main/resources/azurelib.forge.mixins.json @@ -15,11 +15,11 @@ "BlockEntityMixin_AzBlockEntityAnimatorCache", "EntityMixin_AzEntityAnimatorCache", "ItemStackMixin_AzItemAnimatorCache", - "ClientHooksMixin", "ItemRendererAccessor", "MinecraftMixin", "MixinItemRenderer", - "NeoMixinHumanoidArmorLayer", - "TextureManagerMixin" + "TextureManagerMixin", + "ClientHooksMixin", + "NeoMixinHumanoidArmorLayer" ] } \ No newline at end of file From c5475b49b1c2bede34483cf45a0e17c3b6338c5b Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 22:59:20 -0400 Subject: [PATCH 26/40] Update README.md --- README.md | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/README.md b/README.md index 0dfee53a7..d737f152a 100644 --- a/README.md +++ b/README.md @@ -5,32 +5,6 @@ AzureLib represents a branch derived from Geckolib 4.x, serving as an animation engine tailored for Minecraft Mods. It boasts various features, including support for intricate 3D keyframe-driven animations, over 30 different easing functions, concurrent animation capabilities, sound and particle keyframes, event-based keyframes, and numerous other functionalities. Currently, I'll focus on maintaining and supporting AzureLib; no help will be given to Geckolib. -
-Are you a developer and want to use this library in your mod? Add the following to your build.gradle -

- - -``` -repositories { - // The Maven with the mods source - maven {url 'https://maven.azuredoom.com/mods'} -} - -dependencies { - - //Common 1.20.1+ Latest Only - compileOnly "mod.azure.azurelib:azurelib-common-MCVERSION:MODVERSION" - - //Fabric or Quilt - modImplementation "mod.azure.azurelib:azurelib-fabric-MCVERSION:MODVERSION" - - //NeoForge or Forge 1.20.1 - implementation fg.deobf("mod.azure.azurelib:azurelib-neo-MCVERSION:MODVERSION") -} -``` - -
-

Wiki

You can find the AzureLib Wiki here: https://wiki.azuredoom.com/ From 6f91a35c8ab8760ae4b4ccf1d6702c9c4ce539ad Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 22:59:39 -0400 Subject: [PATCH 27/40] Update MDG --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d8504aab8..db7ceb094 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { id 'fabric-loom' version '1.9-SNAPSHOT' apply(false) - id 'net.neoforged.moddev.legacyforge' version '2.0.77' apply(false) + id 'net.neoforged.moddev.legacyforge' version '2.0.88' apply(false) id 'me.modmuss50.mod-publish-plugin' version "${modmuss50_mod_publish_version}" apply(false) } \ No newline at end of file From 44448c069084e77837bc181297dd07c62a9f3a23 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 22:59:48 -0400 Subject: [PATCH 28/40] Update multiloader-common.gradle --- buildSrc/src/main/groovy/multiloader-common.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/buildSrc/src/main/groovy/multiloader-common.gradle b/buildSrc/src/main/groovy/multiloader-common.gradle index 6892a737b..15b726215 100644 --- a/buildSrc/src/main/groovy/multiloader-common.gradle +++ b/buildSrc/src/main/groovy/multiloader-common.gradle @@ -44,6 +44,7 @@ repositories { name = 'BlameJared' url = 'https://maven.blamejared.com' } + maven { url "https://api.modrinth.com/maven" } maven { url "https://cfa2.cursemaven.com" } maven { url "https://maven.terraformersmc.com/" } maven { url "https://maven.terraformersmc.com/releases" } // modmenu From ad4cd531776e09c5d1d7c10b98eb43a307be700a Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Wed, 7 May 2025 22:59:51 -0400 Subject: [PATCH 29/40] Update build.gradle --- neo/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/neo/build.gradle b/neo/build.gradle index 343bb1f85..ca803d209 100644 --- a/neo/build.gradle +++ b/neo/build.gradle @@ -49,12 +49,15 @@ dependencies { annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT:processor") compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")) jarJar(implementation("io.github.llamalad7:mixinextras-forge:0.4.1")) + // Testing a crash (Confirmed no crash with using MixinExtras!) + //modImplementation("maven.modrinth:easy-anvils:v8.0.2-1.20.1-Forge") + //modImplementation("maven.modrinth:puzzles-lib:v8.1.32-1.20.1-Forge") } jar { finalizedBy('reobfJar') manifest.attributes([ - "MixinConfigs": "${mod_id}.mixins.json,${mod_id}.forge.mixins.json" + "MixinConfigs": "${mod_id}.mixins.json,${mod_id}.forge.mixins.json" ]) } From fd6443d428c5bec063b08fc4260e69d2b5825753 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Thu, 8 May 2025 15:17:12 -0400 Subject: [PATCH 30/40] More Deprecated --- .../java/mod/azure/azurelib/builders/AzureGunProperties.java | 1 + common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java | 1 + .../java/mod/azure/azurelib/core/animatable/GeoAnimatable.java | 1 + .../core/animatable/instance/AnimatableInstanceCache.java | 1 + .../animatable/instance/InstancedAnimatableInstanceCache.java | 1 + .../animatable/instance/SingletonAnimatableInstanceCache.java | 1 + .../azure/azurelib/core/animatable/model/CoreBakedGeoModel.java | 1 + .../mod/azure/azurelib/core/animatable/model/CoreGeoBone.java | 1 + .../mod/azure/azurelib/core/animatable/model/CoreGeoModel.java | 1 + .../mod/azure/azurelib/core/animation/AnimatableManager.java | 1 + .../main/java/mod/azure/azurelib/core/animation/Animation.java | 1 + .../mod/azure/azurelib/core/animation/AnimationController.java | 1 + .../mod/azure/azurelib/core/animation/AnimationProcessor.java | 1 + .../java/mod/azure/azurelib/core/animation/AnimationState.java | 1 + .../azurelib/core/animation/ContextAwareAnimatableManager.java | 1 + .../main/java/mod/azure/azurelib/core/animation/EasingType.java | 1 + .../java/mod/azure/azurelib/core/animation/RawAnimation.java | 1 + .../java/mod/azure/azurelib/core/keyframe/AnimationPoint.java | 1 + .../mod/azure/azurelib/core/keyframe/AnimationPointQueue.java | 1 + .../java/mod/azure/azurelib/core/keyframe/BoneAnimation.java | 1 + .../mod/azure/azurelib/core/keyframe/BoneAnimationQueue.java | 1 + .../src/main/java/mod/azure/azurelib/core/keyframe/Keyframe.java | 1 + .../java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java | 1 + .../java/mod/azure/azurelib/core/keyframe/KeyframeStack.java | 1 + .../core/keyframe/event/CustomInstructionKeyframeEvent.java | 1 + .../mod/azure/azurelib/core/keyframe/event/KeyFrameEvent.java | 1 + .../azurelib/core/keyframe/event/ParticleKeyframeEvent.java | 1 + .../azure/azurelib/core/keyframe/event/SoundKeyframeEvent.java | 1 + .../core/keyframe/event/data/CustomInstructionKeyframeData.java | 1 + .../azure/azurelib/core/keyframe/event/data/KeyFrameData.java | 1 + .../azurelib/core/keyframe/event/data/ParticleKeyframeData.java | 1 + .../azurelib/core/keyframe/event/data/SoundKeyframeData.java | 1 + .../src/main/java/mod/azure/azurelib/core/object/DataTicket.java | 1 + .../src/main/java/mod/azure/azurelib/core/object/PlayState.java | 1 + 34 files changed, 34 insertions(+) diff --git a/common/src/main/java/mod/azure/azurelib/builders/AzureGunProperties.java b/common/src/main/java/mod/azure/azurelib/builders/AzureGunProperties.java index d7dc2e2dc..aa727039f 100644 --- a/common/src/main/java/mod/azure/azurelib/builders/AzureGunProperties.java +++ b/common/src/main/java/mod/azure/azurelib/builders/AzureGunProperties.java @@ -10,6 +10,7 @@ * * @author AzureDoom/Boston Vanseghi */ +@Deprecated(forRemoval = true) public class AzureGunProperties { private AzureGunProperties properties; private int ammoCount; diff --git a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java index d7d55b7bc..86e1f2772 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java +++ b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java @@ -35,6 +35,7 @@ * {@link mod.azure.azurelib.core.animation.Animation Animations} and * {@link CoreGeoModel Models} */ +@Deprecated(forRemoval = true) public final class AzureLibCache { private static final Set EXCLUDED_NAMESPACES = ObjectOpenHashSet.of("moreplayermodels", "customnpcs", "gunsrpg", "born_in_chaos_v1"); diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/GeoAnimatable.java b/common/src/main/java/mod/azure/azurelib/core/animatable/GeoAnimatable.java index 3f7dacc2a..38ca97058 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animatable/GeoAnimatable.java +++ b/common/src/main/java/mod/azure/azurelib/core/animatable/GeoAnimatable.java @@ -28,6 +28,7 @@ *

  • {@code GeoItem}
  • * */ +@Deprecated(forRemoval = true) public interface GeoAnimatable { /** * Register your {@link AnimationController AnimationControllers} and their respective animations and conditions. diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/AnimatableInstanceCache.java b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/AnimatableInstanceCache.java index 5a12d9137..b9136e1b6 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/AnimatableInstanceCache.java +++ b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/AnimatableInstanceCache.java @@ -15,6 +15,7 @@ * The base cache class responsible for returning the {@link AnimatableManager} for a given instanceof of a {@link GeoAnimatable}. * This class is abstracted and not intended for direct use. See either {@link SingletonAnimatableInstanceCache} or {@link InstancedAnimatableInstanceCache} */ +@Deprecated(forRemoval = true) public abstract class AnimatableInstanceCache { protected final GeoAnimatable animatable; diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/InstancedAnimatableInstanceCache.java b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/InstancedAnimatableInstanceCache.java index 6ce4b735c..fe9a1fff8 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/InstancedAnimatableInstanceCache.java +++ b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/InstancedAnimatableInstanceCache.java @@ -13,6 +13,7 @@ /** * AnimatableInstanceCache implementation for instantiated objects such as Entities or BlockEntities. Returns a single {@link AnimatableManager} instance per cache. */ +@Deprecated(forRemoval = true) public class InstancedAnimatableInstanceCache extends AnimatableInstanceCache { protected AnimatableManager manager; diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/SingletonAnimatableInstanceCache.java b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/SingletonAnimatableInstanceCache.java index e9cea59f0..716d12650 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/SingletonAnimatableInstanceCache.java +++ b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/SingletonAnimatableInstanceCache.java @@ -15,6 +15,7 @@ /** * AnimatableInstanceCache implementation for singleton/flyweight objects such as Items. Utilises a keyed map to differentiate different instances of the object. */ +@Deprecated(forRemoval = true) public class SingletonAnimatableInstanceCache extends AnimatableInstanceCache { protected final Long2ObjectMap> managers = new Long2ObjectOpenHashMap<>(); diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreBakedGeoModel.java b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreBakedGeoModel.java index 6664b8c81..7e253b4b8 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreBakedGeoModel.java +++ b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreBakedGeoModel.java @@ -14,6 +14,7 @@ * Baked model object for AzureLib models.
    * Mostly an internal placeholder to allow for splitting up core (non-Minecraft) libraries */ +@Deprecated(forRemoval = true) public interface CoreBakedGeoModel { List getBones(); /** diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoBone.java b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoBone.java index c1c4d249e..95cfa23d2 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoBone.java +++ b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoBone.java @@ -15,6 +15,7 @@ * Base class for AzureLib {@link CoreGeoModel model} bones.
    * Mostly a placeholder to allow for splitting up core (non-Minecraft) libraries */ +@Deprecated(forRemoval = true) public interface CoreGeoBone { String getName(); diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoModel.java b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoModel.java index cb05f9d03..81171d9e1 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoModel.java +++ b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoModel.java @@ -18,6 +18,7 @@ * Base class for AzureLib models.
    * Mostly an internal placeholder to allow for splitting up core (non-Minecraft) libraries */ +@Deprecated(forRemoval = true) public interface CoreGeoModel { /** * Get the baked model data for this model based on the provided string location diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/AnimatableManager.java b/common/src/main/java/mod/azure/azurelib/core/animation/AnimatableManager.java index a3e382bf0..45933dee6 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animation/AnimatableManager.java +++ b/common/src/main/java/mod/azure/azurelib/core/animation/AnimatableManager.java @@ -29,6 +29,7 @@ * will have a single instance of {@code AnimatableManager} associated with it.
    * */ +@Deprecated(forRemoval = true) public class AnimatableManager { private final Map boneSnapshotCollection = new Object2ObjectOpenHashMap<>(); private final Map> animationControllers; diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/Animation.java b/common/src/main/java/mod/azure/azurelib/core/animation/Animation.java index fae942533..d8adaae12 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animation/Animation.java +++ b/common/src/main/java/mod/azure/azurelib/core/animation/Animation.java @@ -28,6 +28,7 @@ * A compiled animation instance for use by the {@link AnimationController}
    * Modifications or extensions of a compiled Animation are not supported, and therefore an instance of Animation is considered final and immutable. */ +@Deprecated(forRemoval = true) public record Animation(String name, double length, LoopType loopType, BoneAnimation[] boneAnimations, Keyframes keyFrames) { public record Keyframes(SoundKeyframeData[] sounds, ParticleKeyframeData[] particles, CustomInstructionKeyframeData[] customInstructions) {} diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationController.java b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationController.java index df788df5c..1d168b415 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationController.java +++ b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationController.java @@ -44,6 +44,7 @@ * Each controller can only play a single animation at a time - for example you may have one controller to animate walking, * one to control attacks, one to control size, etc. */ +@Deprecated(forRemoval = true) public class AnimationController { protected final T animatable; protected final String name; diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationProcessor.java b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationProcessor.java index fee3ff5f3..fedb1898c 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationProcessor.java +++ b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationProcessor.java @@ -22,6 +22,7 @@ import mod.azure.azurelib.core.state.BoneSnapshot; import mod.azure.azurelib.core.utils.Interpolations; +@Deprecated(forRemoval = true) public class AnimationProcessor { private final Map bones = new Object2ObjectOpenHashMap<>(); private final CoreGeoModel model; diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationState.java b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationState.java index 99781c56e..6f6e7bd41 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationState.java +++ b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationState.java @@ -20,6 +20,7 @@ * This is where users would set their selected animation to play, * stop the controller, or any number of other animation-related actions. */ +@Deprecated(forRemoval = true) public class AnimationState { private final T animatable; private final float limbSwing; diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/ContextAwareAnimatableManager.java b/common/src/main/java/mod/azure/azurelib/core/animation/ContextAwareAnimatableManager.java index ed374d424..0fb62707e 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animation/ContextAwareAnimatableManager.java +++ b/common/src/main/java/mod/azure/azurelib/core/animation/ContextAwareAnimatableManager.java @@ -20,6 +20,7 @@ * This can be used for things like perspective-dependent animation handling and other similar functionality.
    * This relies entirely on data present in {@link AnimatableManager#extraData} saved to this manager to determine context */ +@Deprecated(forRemoval = true) public abstract class ContextAwareAnimatableManager extends AnimatableManager { private final Map> managers; diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/EasingType.java b/common/src/main/java/mod/azure/azurelib/core/animation/EasingType.java index 732b04d67..d55ade474 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animation/EasingType.java +++ b/common/src/main/java/mod/azure/azurelib/core/animation/EasingType.java @@ -27,6 +27,7 @@ *
    Easings.net
    * Cubic-Bezier.com
    */ +@Deprecated(forRemoval = true) @FunctionalInterface public interface EasingType { Map EASING_TYPES = new ConcurrentHashMap<>(64); diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/RawAnimation.java b/common/src/main/java/mod/azure/azurelib/core/animation/RawAnimation.java index c19c0199d..dab66ee5f 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animation/RawAnimation.java +++ b/common/src/main/java/mod/azure/azurelib/core/animation/RawAnimation.java @@ -27,6 +27,7 @@ * Example usage:
    *
    {@code RawAnimation.begin().thenPlay("action.open_box").thenLoop("state.stay_open")}
    */ +@Deprecated(forRemoval = true) public final class RawAnimation { private final List animationList = new ObjectArrayList<>(); diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPoint.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPoint.java index e7e0b72f6..fbf5a1195 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPoint.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPoint.java @@ -20,6 +20,7 @@ * @param animationEndValue The end value to provide to the animation handling system * @param keyFrame The {@code Nullable} Keyframe */ +@Deprecated(forRemoval = true) public record AnimationPoint(Keyframe keyFrame, double currentTick, double transitionLength, double animationStartValue, double animationEndValue) { @Override public String toString() { diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPointQueue.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPointQueue.java index 713e9b2e8..0aa2af659 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPointQueue.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPointQueue.java @@ -19,6 +19,7 @@ * An {@link AnimationPoint} queue holds a queue of {@code AnimationPoints} which are used in * the {@link mod.azure.azurelib.core.animation.AnimationController} to lerp between values */ +@Deprecated(forRemoval = true) public final class AnimationPointQueue extends LinkedList { @Serial private static final long serialVersionUID = 5472797438476621193L; diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimation.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimation.java index a82f648bf..e4b1684bf 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimation.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimation.java @@ -23,6 +23,7 @@ * @param positionKeyFrames The deserialized position {@code Keyframe} stack * @param scaleKeyFrames The deserialized scale {@code Keyframe} stack */ +@Deprecated(forRemoval = true) public record BoneAnimation(String boneName, KeyframeStack> rotationKeyFrames, KeyframeStack> positionKeyFrames, diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimationQueue.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimationQueue.java index 5bd33545c..c6b756cea 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimationQueue.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimationQueue.java @@ -19,6 +19,7 @@ * A bone pseudo-stack for bone animation positions, scales, and rotations. * Animation points are calculated then pushed onto their respective queues to be used for transformations in rendering */ +@Deprecated(forRemoval = true) public record BoneAnimationQueue(CoreGeoBone bone, AnimationPointQueue rotationXQueue, AnimationPointQueue rotationYQueue, AnimationPointQueue rotationZQueue, AnimationPointQueue positionXQueue, AnimationPointQueue positionYQueue, AnimationPointQueue positionZQueue, AnimationPointQueue scaleXQueue, AnimationPointQueue scaleYQueue, diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/Keyframe.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/Keyframe.java index 721ffc3cb..27d036f33 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/Keyframe.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/Keyframe.java @@ -27,6 +27,7 @@ * @param easingType The {@code EasingType} to use for transformations * @param easingArgs The arguments to provide to the easing calculation */ +@Deprecated(forRemoval = true) public record Keyframe(double length, T startValue, T endValue, EasingType easingType, List easingArgs) { public Keyframe(double length, T startValue, T endValue) { this(length, startValue, endValue, EasingType.LINEAR); diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java index 24047210c..2a8827357 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java @@ -19,5 +19,6 @@ * @param keyframe The {@code Keyframe} at the tick time * @param startTick The animation tick time at the start of this {@code Keyframe} */ +@Deprecated(forRemoval = true) public record KeyframeLocation>(T keyframe, double startTick) { } //TODO: public record KeyframeLocation(Keyframe keyframe, double startTick) { } \ No newline at end of file diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeStack.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeStack.java index e594bacc0..34a2df5f0 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeStack.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeStack.java @@ -19,6 +19,7 @@ /** * Stores a triplet of {@link Keyframe Keyframes} in an ordered stack */ +@Deprecated(forRemoval = true) public record KeyframeStack>(List xKeyframes, List yKeyframes, List zKeyframes) { public KeyframeStack() { this(new ObjectArrayList<>(), new ObjectArrayList<>(), new ObjectArrayList<>()); diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/CustomInstructionKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/CustomInstructionKeyframeEvent.java index 6a54b6f2b..b30757c41 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/CustomInstructionKeyframeEvent.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/CustomInstructionKeyframeEvent.java @@ -20,6 +20,7 @@ * The {@link KeyFrameEvent} specific to the {@link AnimationController#customKeyframeHandler}.
    * Called when a custom instruction keyframe is encountered */ +@Deprecated(forRemoval = true) public class CustomInstructionKeyframeEvent extends KeyFrameEvent { public CustomInstructionKeyframeEvent(T entity, double animationTick, AnimationController controller, CustomInstructionKeyframeData customInstructionKeyframeData) { diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/KeyFrameEvent.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/KeyFrameEvent.java index ce6b60373..083a115df 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/KeyFrameEvent.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/KeyFrameEvent.java @@ -24,6 +24,7 @@ * @see ParticleKeyframeEvent * @see SoundKeyframeEvent */ +@Deprecated(forRemoval = true) public abstract class KeyFrameEvent { private final T animatable; private final double animationTick; diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/ParticleKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/ParticleKeyframeEvent.java index 738e7a510..3abe01798 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/ParticleKeyframeEvent.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/ParticleKeyframeEvent.java @@ -15,6 +15,7 @@ * The {@link KeyFrameEvent} specific to the {@link AnimationController#particleKeyframeHandler}.
    * Called when a particle instruction keyframe is encountered */ +@Deprecated(forRemoval = true) public class ParticleKeyframeEvent extends KeyFrameEvent { public ParticleKeyframeEvent(T animatable, double animationTick, AnimationController controller, ParticleKeyframeData particleKeyFrameData) { super(animatable, animationTick, controller, particleKeyFrameData); diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/SoundKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/SoundKeyframeEvent.java index b2c98a3ba..2f5f1e035 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/SoundKeyframeEvent.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/SoundKeyframeEvent.java @@ -20,6 +20,7 @@ * The {@link KeyFrameEvent} specific to the {@link AnimationController#soundKeyframeHandler}.
    * Called when a sound instruction keyframe is encountered */ +@Deprecated(forRemoval = true) public class SoundKeyframeEvent extends KeyFrameEvent { /** * This stores all the fields that are needed in the AnimationTestEvent diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/CustomInstructionKeyframeData.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/CustomInstructionKeyframeData.java index 6a4ff4597..42a64e126 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/CustomInstructionKeyframeData.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/CustomInstructionKeyframeData.java @@ -14,6 +14,7 @@ /** * Custom instruction {@link Keyframe} instruction holder */ +@Deprecated(forRemoval = true) public class CustomInstructionKeyframeData extends KeyFrameData { private final String instructions; diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/KeyFrameData.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/KeyFrameData.java index 0f9aa2ec9..1c83c7660 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/KeyFrameData.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/KeyFrameData.java @@ -21,6 +21,7 @@ * @see ParticleKeyframeData * @see SoundKeyframeData */ +@Deprecated(forRemoval = true) public abstract class KeyFrameData { private final double startTick; diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/ParticleKeyframeData.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/ParticleKeyframeData.java index 80910cad1..b4f6c62bc 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/ParticleKeyframeData.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/ParticleKeyframeData.java @@ -14,6 +14,7 @@ /** * Particle {@link Keyframe} instruction holder */ +@Deprecated(forRemoval = true) public class ParticleKeyframeData extends KeyFrameData { private final String effect; private final String locator; diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/SoundKeyframeData.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/SoundKeyframeData.java index a2638958f..4bc7ac674 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/SoundKeyframeData.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/SoundKeyframeData.java @@ -14,6 +14,7 @@ /** * Sound {@link Keyframe} instruction holder */ +@Deprecated(forRemoval = true) public class SoundKeyframeData extends KeyFrameData { private final String sound; diff --git a/common/src/main/java/mod/azure/azurelib/core/object/DataTicket.java b/common/src/main/java/mod/azure/azurelib/core/object/DataTicket.java index 4f1a87f1c..b4233d86a 100644 --- a/common/src/main/java/mod/azure/azurelib/core/object/DataTicket.java +++ b/common/src/main/java/mod/azure/azurelib/core/object/DataTicket.java @@ -13,6 +13,7 @@ /** * Ticket object to define a typed data object */ +@Deprecated(forRemoval = true) public class DataTicket { private final String id; private final Class objectType; diff --git a/common/src/main/java/mod/azure/azurelib/core/object/PlayState.java b/common/src/main/java/mod/azure/azurelib/core/object/PlayState.java index 25f4f4736..6e4b39cd0 100644 --- a/common/src/main/java/mod/azure/azurelib/core/object/PlayState.java +++ b/common/src/main/java/mod/azure/azurelib/core/object/PlayState.java @@ -10,6 +10,7 @@ /** * State enum to define whether an {@link mod.azure.azurelib.core.animation.AnimationController} should continue or stop */ +@Deprecated(forRemoval = true) public enum PlayState { CONTINUE, STOP From f4d86842ee20a887b4e0bc1a4604c11fada6ac23 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Thu, 8 May 2025 22:56:31 -0400 Subject: [PATCH 31/40] Now properly hide player outlayers on equipping armor --- .../mixins/FabricMixinHumanoidArmorLayer.java | 27 +++++++++++++++++++ .../resources/META-INF/accesstransformer.cfg | 1 + common/src/main/resources/azurelib.aw | 1 + .../mixins/NeoMixinHumanoidArmorLayer.java | 27 +++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java b/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java index abd9f2f6c..0bd9ea632 100644 --- a/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java +++ b/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java @@ -9,13 +9,18 @@ import mod.azure.azurelib.renderer.GeoArmorRenderer; import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.PlayerModel; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; +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.CallbackInfo; @@ -84,8 +89,30 @@ public abstract class FabricMixinHumanoidArmorLayer playerModel) { + switch (equipmentSlot) { + case HEAD -> { + playerModel.hat.visible = false; + playerModel.ear.visible = false; + } + case CHEST -> { + playerModel.jacket.visible = false; + playerModel.rightSleeve.visible = false; + playerModel.leftSleeve.visible = false; + } + case LEGS -> { + playerModel.leftPants.visible = false; + playerModel.rightPants.visible = false; + } + } + } + } } diff --git a/common/src/main/resources/META-INF/accesstransformer.cfg b/common/src/main/resources/META-INF/accesstransformer.cfg index 66e689e1d..726c9f3cb 100644 --- a/common/src/main/resources/META-INF/accesstransformer.cfg +++ b/common/src/main/resources/META-INF/accesstransformer.cfg @@ -24,6 +24,7 @@ public net.minecraft.client.model.AgeableListModel f_170339_ # babyZHeadOffset public net.minecraft.client.model.AgeableListModel f_102010_ # babyHeadScale public net.minecraft.client.model.AgeableListModel f_102011_ # babyBodyScale public net.minecraft.client.model.AgeableListModel f_102012_ # bodyYOffset +public net.minecraft.client.model.PlayerModel f_103379_ # ear public-f net.minecraft.client.renderer.LevelRenderer f_109464_ # renderBuffers public-f net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer m_289609_(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/HumanoidModel;ZFFFLjava/lang/String;)V # renderModel diff --git a/common/src/main/resources/azurelib.aw b/common/src/main/resources/azurelib.aw index d38bf4f04..42c869fb5 100644 --- a/common/src/main/resources/azurelib.aw +++ b/common/src/main/resources/azurelib.aw @@ -25,6 +25,7 @@ accessible field net/minecraft/client/model/AgeableListModel babyZHeadOffset F accessible field net/minecraft/client/model/AgeableListModel babyHeadScale F accessible field net/minecraft/client/model/AgeableListModel babyBodyScale F accessible field net/minecraft/client/model/AgeableListModel bodyYOffset F +accessible field net/minecraft/client/model/PlayerModel ear Lnet/minecraft/client/model/geom/ModelPart; accessible field net/minecraft/client/renderer/LevelRenderer renderBuffers Lnet/minecraft/client/renderer/RenderBuffers; mutable field net/minecraft/client/renderer/LevelRenderer renderBuffers Lnet/minecraft/client/renderer/RenderBuffers; diff --git a/neo/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java b/neo/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java index 88e8f41f8..c7fd6029e 100644 --- a/neo/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java +++ b/neo/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java @@ -5,13 +5,18 @@ import com.llamalad7.mixinextras.sugar.ref.LocalRef; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.PlayerModel; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; +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.CallbackInfo; @@ -86,8 +91,30 @@ public class NeoMixinHumanoidArmorLayer { + playerModel.hat.visible = false; + playerModel.ear.visible = false; + } + case CHEST -> { + playerModel.jacket.visible = false; + playerModel.rightSleeve.visible = false; + playerModel.leftSleeve.visible = false; + } + case LEGS -> { + playerModel.leftPants.visible = false; + playerModel.rightPants.visible = false; + } + } + } + } } From 8d104a4deea35d7ca7da59e5f5f224b72d1db6ca Mon Sep 17 00:00:00 2001 From: Modrome Date: Mon, 12 May 2025 20:12:22 -0400 Subject: [PATCH 32/40] Fix spinning entities --- .../azurelib/ai/pathing/AzureNavigation.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java index 96119c668..da0c08fb4 100644 --- a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java +++ b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java @@ -112,12 +112,20 @@ public boolean moveTo(Entity entity, double d) { return true; } - @Override - public void tick() { - super.tick(); + // If the entity is below 0.8 units in width, the pathing tends to "fail," causing issues like + // MC-226637 to take place. By forcing the width check to calculate at least a MINIMUM value of 1.0, + // we effectively remove this bug for smaller entities, while keeping larger entity navigation behaviors + // untouched. Might be a performance improvement, as the hitbox and AI won't update to "spin." -Modrome + public float getMinimumWidth() { + return Math.max(this.mob.getBbWidth(),1.0F); //Return whichever value is greater, for small entities, this returns 1.0 no matter what, fixing our spinning entities. + } + + @Override + public void tick() { + super.tick(); if (this.isDone()) { if (this.pathToPosition != null) { - if (this.pathToPosition.closerToCenterThan(this.mob.position(), this.mob.getBbWidth()) || this.mob.getY() > (double)this.pathToPosition.getY() && BlockPos.containing(this.pathToPosition.getX(), this.mob.getY(), this.pathToPosition.getZ()).closerToCenterThan(this.mob.position(), this.mob.getBbWidth())) { + if (this.pathToPosition.closerToCenterThan(this.mob.position(), getMinimumWidth()) || this.mob.getY() > (double)this.pathToPosition.getY() && BlockPos.containing(this.pathToPosition.getX(), this.mob.getY(), this.pathToPosition.getZ()).closerToCenterThan(this.mob.position(), getMinimumWidth())) { this.pathToPosition = null; } else { this.mob.getMoveControl().setWantedPosition(this.pathToPosition.getX(), this.pathToPosition.getY(), this.pathToPosition.getZ(), this.speedModifier); @@ -125,9 +133,9 @@ public void tick() { } return; } - if (this.getTargetPos() != null) - this.mob.getLookControl().setLookAt(this.getTargetPos().getX(), this.getTargetPos().getY(), this.getTargetPos().getZ()); - } + if (this.getTargetPos() != null) + this.mob.getLookControl().setLookAt(this.getTargetPos().getX(), this.getTargetPos().getY(), this.getTargetPos().getZ()); + } private boolean isAt(Path path, float threshold) { final Vec3 pathPos = path.getNextEntityPos(this.mob); From e85bb62e28a2d01140994b45e4c2a4482c2847f1 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Tue, 13 May 2025 12:08:33 -0400 Subject: [PATCH 33/40] Convert to Java Doc --- .../azurelib/ai/pathing/AzureNavigation.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java index da0c08fb4..0ed7c9b65 100644 --- a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java +++ b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java @@ -112,11 +112,22 @@ public boolean moveTo(Entity entity, double d) { return true; } - // If the entity is below 0.8 units in width, the pathing tends to "fail," causing issues like - // MC-226637 to take place. By forcing the width check to calculate at least a MINIMUM value of 1.0, - // we effectively remove this bug for smaller entities, while keeping larger entity navigation behaviors - // untouched. Might be a performance improvement, as the hitbox and AI won't update to "spin." -Modrome - public float getMinimumWidth() { + /** + * Ensures a minimum width of 1.0 for entities, addressing an issue + * where smaller entities (< 0.8 units in width) encounter pathfinding + * failures. This resolves bugs such as MC-226637, where small entities + * end up "spinning" due to improper navigation logic. + *

    + * By enforcing a minimum calculated width of 1.0, this method prevents + * the pathfinding system from failing on smaller entities, while leaving + * the behavior of larger entities unchanged. It may also reduce + * performance overhead by preventing frequent hitbox and AI updates + * caused by entity spinning. + * + * @author Modrome + * @return the maximum of the entity's actual width and 1.0, ensuring a minimum width for correct pathing. + */ + public float getMinimumWidth() { return Math.max(this.mob.getBbWidth(),1.0F); //Return whichever value is greater, for small entities, this returns 1.0 no matter what, fixing our spinning entities. } From 7ef1efaee9b4cd68c91524eda56704a3762557fe Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Tue, 13 May 2025 12:08:40 -0400 Subject: [PATCH 34/40] Lint --- .../java/mod/azure/azurelib/ai/pathing/AzureNavigation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java index 0ed7c9b65..3086c8101 100644 --- a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java +++ b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java @@ -136,7 +136,7 @@ public void tick() { super.tick(); if (this.isDone()) { if (this.pathToPosition != null) { - if (this.pathToPosition.closerToCenterThan(this.mob.position(), getMinimumWidth()) || this.mob.getY() > (double)this.pathToPosition.getY() && BlockPos.containing(this.pathToPosition.getX(), this.mob.getY(), this.pathToPosition.getZ()).closerToCenterThan(this.mob.position(), getMinimumWidth())) { + if (this.pathToPosition.closerToCenterThan(this.mob.position(), getMinimumWidth()) || this.mob.getY() > this.pathToPosition.getY() && BlockPos.containing(this.pathToPosition.getX(), this.mob.getY(), this.pathToPosition.getZ()).closerToCenterThan(this.mob.position(), getMinimumWidth())) { this.pathToPosition = null; } else { this.mob.getMoveControl().setWantedPosition(this.pathToPosition.getX(), this.pathToPosition.getY(), this.pathToPosition.getZ(), this.speedModifier); From 57e1593725e15f9f84955370e30dafd3e95359fb Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Thu, 15 May 2025 10:24:19 -0400 Subject: [PATCH 35/40] Back port 1.21.1 changes/fixes. --- .../azure/azurelib/cache/object/GeoCube.java | 1 - .../azure/azurelib/cache/object/GeoQuad.java | 1 - .../azurelib/cache/object/GeoVertex.java | 1 - .../rewrite/render/AzRendererConfig.java | 121 ++++++++++++++---- .../render/AzRendererPipelineContext.java | 22 ++++ .../render/armor/AzArmorRendererConfig.java | 60 +++++++-- .../render/armor/AzArmorRendererPipeline.java | 8 +- .../armor/AzArmorRendererPipelineContext.java | 32 ++++- .../block/AzBlockEntityRendererConfig.java | 59 +++++++-- .../block/AzBlockEntityRendererPipeline.java | 7 +- .../render/entity/AzEntityRenderer.java | 2 + .../render/entity/AzEntityRendererConfig.java | 95 ++++++++++++-- .../entity/AzEntityRendererPipeline.java | 7 +- .../render/item/AzItemRendererConfig.java | 59 +++++++-- .../render/item/AzItemRendererPipeline.java | 9 +- .../item/AzItemRendererPipelineContext.java | 18 ++- .../testing/entity/MarauderRenderer.java | 2 +- 17 files changed, 425 insertions(+), 79 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java index 11d2139bc..c2f36f8ad 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java +++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java @@ -12,5 +12,4 @@ /** * Baked cuboid for a {@link GeoBone} */ -@Deprecated(forRemoval = true) public record GeoCube(GeoQuad[] quads, Vec3 pivot, Vec3 rotation, Vec3 size, double inflate, boolean mirror) {} diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java index 4758c7483..a35821092 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java +++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java @@ -15,7 +15,6 @@ /** * Quad data holder */ -@Deprecated(forRemoval = true) public record GeoQuad( GeoVertex[] vertices, Vector3f normal, diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java index 0c4ba1732..aad4c474d 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java +++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java @@ -15,7 +15,6 @@ * @param texU The texture U coordinate * @param texV The texture V coordinate */ -@Deprecated(forRemoval = true) public record GeoVertex(Vector3f position, float texU, float texV) { public GeoVertex(double x, double y, double z) { this(new Vector3f((float)x, (float)y, (float)z), 0, 0); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java index e8872dd56..98772a1cc 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.function.Function; import java.util.function.Supplier; +import java.util.function.UnaryOperator; /** * The {@code AzRendererConfig} class is a configuration class used for defining rendering configurations for generic @@ -27,28 +28,31 @@ public class AzRendererConfig { protected final Function renderTypeFunction; - private final Function, AzRendererPipelineContext> preRenderEntry; + private final UnaryOperator> preRenderEntry; - private final Function, AzRendererPipelineContext> postRenderEntry; + private final UnaryOperator> postRenderEntry; protected final List> renderLayers; protected final Function textureLocationProvider; - protected final float scaleHeight; + private final Function alphaFunction; - protected final float scaleWidth; + private final Function scaleHeight; + + private final Function scaleWidth; public AzRendererConfig( Supplier> animatorProvider, Function modelLocationProvider, Function renderTypeFunction, List> renderLayers, - Function, AzRendererPipelineContext> preRenderEntry, - Function, AzRendererPipelineContext> postRenderEntry, + UnaryOperator> preRenderEntry, + UnaryOperator> postRenderEntry, Function textureLocationProvider, - float scaleHeight, - float scaleWidth + Function alphaFunction, + Function scaleHeight, + Function scaleWidth ) { this.animatorProvider = animatorProvider; this.modelLocationProvider = modelLocationProvider; @@ -59,6 +63,7 @@ public AzRendererConfig( this.textureLocationProvider = textureLocationProvider; this.scaleHeight = scaleHeight; this.scaleWidth = scaleWidth; + this.alphaFunction = alphaFunction; } public @Nullable AzAnimator createAnimator() { @@ -89,12 +94,16 @@ public AzRendererPipelineContext postRenderEntry(AzRendererPipelineContext return postRenderEntry.apply(animatable); } - public float scaleHeight() { - return scaleHeight; + public float alpha(T entity) { + return alphaFunction.apply(entity); + } + + public float scaleHeight(T entity) { + return scaleHeight.apply(entity); } - public float scaleWidth() { - return scaleWidth; + public float scaleWidth(T entity) { + return scaleWidth.apply(entity); } public static class Builder { @@ -105,17 +114,19 @@ public static class Builder { private final List> renderLayers; - private Function, AzRendererPipelineContext> preRenderEntry; + protected UnaryOperator> preRenderEntry; - private Function, AzRendererPipelineContext> postRenderEntry; + protected UnaryOperator> postRenderEntry; - private final Function textureLocationProvider; + protected final Function textureLocationProvider; - private Supplier<@Nullable AzAnimator> animatorProvider; + protected Supplier<@Nullable AzAnimator> animatorProvider; - private float scaleHeight; + protected Function alphaFunction; - private float scaleWidth; + protected Function scaleHeight; + + protected Function scaleWidth; protected Builder( Function modelLocationProvider, @@ -128,8 +139,9 @@ protected Builder( this.preRenderEntry = $ -> $; this.postRenderEntry = $ -> $; this.textureLocationProvider = textureLocationProvider; - this.scaleHeight = 1; - this.scaleWidth = 1; + this.alphaFunction = $ -> 1.0F; + this.scaleHeight = $ -> 1.0F; + this.scaleWidth = $ -> 1.0F; } /** @@ -153,16 +165,46 @@ public Builder addRenderLayer(AzRenderLayer renderLayer) { return this; } - public Builder setPrerenderEntry(Function, AzRendererPipelineContext> preRenderEntry) { + public Builder setPrerenderEntry( + UnaryOperator> preRenderEntry + ) { this.preRenderEntry = preRenderEntry; return this; } - public Builder setPostRenderEntry(Function, AzRendererPipelineContext> postRenderEntry) { + public Builder setPostRenderEntry( + UnaryOperator> postRenderEntry + ) { this.postRenderEntry = postRenderEntry; return this; } + /** + * Sets the alpha value provider for the builder. The alpha value determines the opacity level of the rendered + * object and is calculated dynamically based on the specified function. + * + * @param alphaFunction a {@link Function} that takes an object of type {@code T} and returns a {@code Float} + * value representing the alpha (opacity) level, where 0.0 is fully transparent and 1.0 is + * fully opaque + * @return the updated {@code Builder} instance for chaining configuration methods + */ + public Builder setAlpha(Function alphaFunction) { + this.alphaFunction = alphaFunction; + return this; + } + + /** + * Sets the alpha transparency level for the builder, which determines the level of transparency to be applied. + * + * @param alpha the alpha transparency value to set, where 0.0 represents fully transparent and 1.0 represents + * fully opaque + * @return the updated {@code Builder} instance for chaining configuration methods + */ + public Builder setAlpha(float alpha) { + this.alphaFunction = $ -> alpha; + return this; + } + /** * Sets the scaling factor uniformly for both width and height dimensions. * @@ -181,8 +223,38 @@ public Builder setScale(float scale) { * @return the updated builder instance for chaining operations */ public Builder setScale(float scaleWidth, float scaleHeight) { - this.scaleHeight = scaleHeight; - this.scaleWidth = scaleWidth; + this.scaleHeight = $ -> scaleHeight; + this.scaleWidth = $ -> scaleWidth; + return this; + } + + /** + * Sets the scaling function for both the width and height dimensions of the target object. The provided + * function dynamically calculates scaling factors based on the input object of type {@code T}. + * + * @param scaleFunction a {@link Function} that takes an object of type {@code T} and returns a {@code Float} + * value representing the scaling factor to be applied uniformly to both width and height + * @return the updated {@code Builder} instance for chaining configuration methods + */ + public Builder setScale(Function scaleFunction) { + this.scaleHeight = scaleFunction; + this.scaleWidth = scaleFunction; + return this; + } + + /** + * Sets the scaling functions for height and width dimensions. These functions dynamically calculate scaling + * factors based on the input object of type {@code T}. + * + * @param scaleHeightFunction a {@link Function} that takes an object of type {@code T} and returns a + * {@code Float} representing the scaling factor for the height dimension + * @param scaleWidthFunction a {@link Function} that takes an object of type {@code T} and returns a + * {@code Float} representing the scaling factor for the width dimension + * @return the updated {@code Builder} instance for chaining configuration methods + */ + public Builder setScale(Function scaleHeightFunction, Function scaleWidthFunction) { + this.scaleHeight = scaleHeightFunction; + this.scaleWidth = scaleWidthFunction; return this; } @@ -202,6 +274,7 @@ public AzRendererConfig build() { preRenderEntry, postRenderEntry, textureLocationProvider, + alphaFunction, scaleHeight, scaleWidth ); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java index 0778fc933..24fb83a5e 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java @@ -165,6 +165,28 @@ public float alpha() { return alpha; } + public void setRed(float red) { + this.red = red; + } + + public void setGreen(float green) { + this.green = green; + } + + public void setBlue(float blue) { + this.blue = blue; + } + + public void setAlpha(float alpha) { + this.alpha = alpha; + } + + public void setColor(Color color) { + this.red = color.getRedFloat(); + this.green = color.getGreenFloat(); + this.blue = color.getBlueFloat(); + } + public int packedLight() { return packedLight; } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java index 112c8d163..8b715f2cb 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.function.Function; import java.util.function.Supplier; +import java.util.function.UnaryOperator; public class AzArmorRendererConfig extends AzRendererConfig { @@ -25,11 +26,12 @@ private AzArmorRendererConfig( Function modelLocationProvider, Function renderTypeProvider, List> renderLayers, - Function, AzRendererPipelineContext> preRenderEntry, - Function, AzRendererPipelineContext> postRenderEntry, + UnaryOperator> preRenderEntry, + UnaryOperator> postRenderEntry, Function textureLocationProvider, - float scaleHeight, - float scaleWidth + Function alphaFunction, + Function scaleHeight, + Function scaleWidth ) { super( animatorProvider, @@ -39,6 +41,7 @@ private AzArmorRendererConfig( preRenderEntry, postRenderEntry, textureLocationProvider, + alphaFunction, scaleHeight, scaleWidth ); @@ -97,8 +100,48 @@ public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> ani } @Override - public Builder setPrerenderEntry(Function, AzRendererPipelineContext> preRenderEntry) { - return (Builder) super.setPrerenderEntry(preRenderEntry); + public Builder setPrerenderEntry(UnaryOperator> preRenderEntry + ) { + return (AzArmorRendererConfig.Builder) super.setPrerenderEntry(preRenderEntry); + } + + @Override + public Builder setPostRenderEntry(UnaryOperator> preRenderEntry + ) { + return (AzArmorRendererConfig.Builder) super.setPostRenderEntry(preRenderEntry); + } + + @Override + public Builder setAlpha(Function alphaFunction) { + return (AzArmorRendererConfig.Builder) super.setAlpha(alphaFunction); + } + + @Override + public Builder setAlpha(float alpha) { + return (AzArmorRendererConfig.Builder) super.setAlpha(alpha); + } + + @Override + public Builder setScale(Function scaleFunction) { + return (AzArmorRendererConfig.Builder) super.setScale(scaleFunction); + } + + @Override + public Builder setScale( + Function scaleHeightFunction, + Function scaleWidthFunction + ) { + return (AzArmorRendererConfig.Builder) super.setScale(scaleHeightFunction, scaleWidthFunction); + } + + @Override + public Builder setScale(float scale) { + return (AzArmorRendererConfig.Builder) super.setScale(scale); + } + + @Override + public Builder setScale(float scaleWidth, float scaleHeight) { + return (AzArmorRendererConfig.Builder) super.setScale(scaleWidth, scaleHeight); } public Builder setBoneProvider(AzArmorBoneProvider boneProvider) { @@ -119,8 +162,9 @@ public AzArmorRendererConfig build() { baseConfig::preRenderEntry, baseConfig::postRenderEntry, baseConfig::textureLocation, - baseConfig.scaleHeight(), - baseConfig.scaleWidth() + baseConfig::alpha, + baseConfig::scaleHeight, + baseConfig::scaleWidth ); } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java index 764c54bb3..306780d75 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java @@ -53,8 +53,8 @@ public void preRender(AzRendererPipelineContext context, boolean isRe var boneContext = armorContext.boneContext(); var config = config(); var currentSlot = armorContext.currentSlot(); - var scaleWidth = config.scaleWidth(); - var scaleHeight = config.scaleHeight(); + var scaleWidth = config.scaleWidth(context.animatable()); + var scaleHeight = config.scaleHeight(context.animatable()); var animatable = armorContext.animatable(); var model = armorRenderer.provider().provideBakedModel(animatable); @@ -69,6 +69,10 @@ public void preRender(AzRendererPipelineContext context, boolean isRe scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); boneContext.applyBoneVisibilityBySlot(currentSlot); + if (config.alpha(context.animatable()) < 1) { + armorContext.setAlpha(config.alpha(context.animatable())); + armorContext.setTranslucent(true); + } config.preRenderEntry(context); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipelineContext.java index e6fa0dbb6..a1004f5d4 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipelineContext.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipelineContext.java @@ -1,5 +1,6 @@ package mod.azure.azurelib.rewrite.render.armor; +import mod.azure.azurelib.core.object.Color; import mod.azure.azurelib.rewrite.render.AzRendererPipeline; import mod.azure.azurelib.rewrite.render.AzRendererPipelineContext; import mod.azure.azurelib.rewrite.render.armor.bone.AzArmorBoneContext; @@ -7,8 +8,10 @@ import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.ItemTags; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.DyeableArmorItem; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,6 +28,8 @@ public class AzArmorRendererPipelineContext extends AzRendererPipelineContext rendererPipeline) { super(rendererPipeline); this.baseModel = null; @@ -41,7 +46,9 @@ public AzArmorRendererPipelineContext(AzRendererPipeline rendererPipe @Nullable MultiBufferSource bufferSource, float partialTick ) { - return RenderType.armorCutoutNoCull(texture); + return translucent + ? RenderType.itemEntityTranslucentCull(texture) + : RenderType.armorCutoutNoCull(texture); } public void prepare( @@ -56,6 +63,29 @@ public void prepare( this.currentSlot = slot; } + /** + * Sets whether the rendering pipeline should render with a translucent effect or not. + * + * @param translucent A boolean value indicating whether to enable or disable translucency. If true, the rendering + * pipeline will apply a translucent effect to rendered elements. If false, it will render with + * an opaque effect. + */ + public void setTranslucent(boolean translucent) { + this.translucent = translucent; + } + + /** + * Gets a tint-applying color to render the given animatable with + *

    + * Returns {@link Color#WHITE} by default + */ + @Override + public Color getRenderColor(ItemStack animatable, float partialTick, int packedLight) { + return this.currentStack.getItem() instanceof DyeableArmorItem dyeableArmorItem + ? Color.ofOpaque(dyeableArmorItem.getColor(animatable)) + : Color.WHITE; + } + public HumanoidModel baseModel() { return baseModel; } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java index 35ff8b6aa..2b6d144f0 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.function.Function; import java.util.function.Supplier; +import java.util.function.UnaryOperator; /** * The {@code AzBlockEntityRendererConfig} class is a specialized configuration for rendering block entities. It extends @@ -27,11 +28,12 @@ private AzBlockEntityRendererConfig( Function modelLocationProvider, Function renderTypeFunction, List> renderLayers, - Function, AzRendererPipelineContext> preRenderEntry, - Function, AzRendererPipelineContext> postRenderEntry, + UnaryOperator> preRenderEntry, + UnaryOperator> postRenderEntry, Function textureLocationProvider, - float scaleHeight, - float scaleWidth + Function alphaFunction, + Function scaleHeight, + Function scaleWidth ) { super( animatorProvider, @@ -41,6 +43,7 @@ private AzBlockEntityRendererConfig( preRenderEntry, postRenderEntry, textureLocationProvider, + alphaFunction, scaleHeight, scaleWidth ); @@ -85,8 +88,17 @@ public Builder setRenderType(Function renderTypeProvider) { } @Override - public Builder setPrerenderEntry(Function, AzRendererPipelineContext> preRenderEntry) { - return (Builder) super.setPrerenderEntry(preRenderEntry); + public Builder setPrerenderEntry( + UnaryOperator> preRenderEntry + ) { + return (AzBlockEntityRendererConfig.Builder) super.setPrerenderEntry(preRenderEntry); + } + + @Override + public Builder setPostRenderEntry( + UnaryOperator> preRenderEntry + ) { + return (AzBlockEntityRendererConfig.Builder) super.setPostRenderEntry(preRenderEntry); } @Override @@ -94,6 +106,36 @@ public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animator return (Builder) super.setAnimatorProvider(animatorProvider); } + @Override + public Builder setAlpha(Function alphaFunction) { + return (AzBlockEntityRendererConfig.Builder) super.setAlpha(alphaFunction); + } + + @Override + public Builder setAlpha(float alpha) { + return (AzBlockEntityRendererConfig.Builder) super.setAlpha(alpha); + } + + @Override + public Builder setScale(Function scaleFunction) { + return (AzBlockEntityRendererConfig.Builder) super.setScale(scaleFunction); + } + + @Override + public Builder setScale(Function scaleHeightFunction, Function scaleWidthFunction) { + return (AzBlockEntityRendererConfig.Builder) super.setScale(scaleHeightFunction, scaleWidthFunction); + } + + @Override + public Builder setScale(float scale) { + return (AzBlockEntityRendererConfig.Builder) super.setScale(scale); + } + + @Override + public Builder setScale(float scaleWidth, float scaleHeight) { + return (AzBlockEntityRendererConfig.Builder) super.setScale(scaleWidth, scaleHeight); + } + @Override public AzBlockEntityRendererConfig build() { var baseConfig = super.build(); @@ -106,8 +148,9 @@ public AzBlockEntityRendererConfig build() { baseConfig::preRenderEntry, baseConfig::postRenderEntry, baseConfig::textureLocation, - baseConfig.scaleHeight(), - baseConfig.scaleWidth() + baseConfig::alpha, + baseConfig::scaleHeight, + baseConfig::scaleWidth ); } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java index f22eae844..ad8e0c1ea 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java @@ -68,9 +68,12 @@ public void preRender(AzRendererPipelineContext context, boolean isReRender) var poseStack = context.poseStack(); this.entityRenderTranslations.set(poseStack.last().pose()); - var scaleWidth = config.scaleWidth(); - var scaleHeight = config.scaleHeight(); + var scaleWidth = config.scaleWidth(context.animatable()); + var scaleHeight = config.scaleHeight(context.animatable()); scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); + if (config.alpha(context.animatable()) < 1) { + context.setAlpha(config.alpha(context.animatable())); + } config.preRenderEntry(context); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java index b3699c9c2..293719954 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java @@ -76,6 +76,8 @@ public void render( cachedEntityAnimator.setActiveModel(azBakedModel); } + this.shadowRadius = config.shadowRadius(entity); + // Point the renderer's current animator reference to the cached entity animator before rendering. reusedAzEntityAnimator = cachedEntityAnimator; diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java index 8387bf460..d5d563f7f 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.function.Function; import java.util.function.Supplier; +import java.util.function.UnaryOperator; /** * Configures the rendering behavior for custom entities in the game. This extends {@link AzRendererConfig}, adding @@ -23,17 +24,21 @@ public class AzEntityRendererConfig extends AzRendererConfig deathMaxRotationProvider; + private final Function shadowRadius; + private AzEntityRendererConfig( Supplier> animatorProvider, Function deathMaxRotationProvider, + Function shadowRadius, Function modelLocationProvider, Function renderTypeFunction, List> renderLayers, - Function, AzRendererPipelineContext> preRenderEntry, - Function, AzRendererPipelineContext> postRenderEntry, + UnaryOperator> preRenderEntry, + UnaryOperator> postRenderEntry, Function textureLocationProvider, - float scaleHeight, - float scaleWidth + Function alphaFunction, + Function scaleHeight, + Function scaleWidth ) { super( animatorProvider, @@ -43,16 +48,22 @@ private AzEntityRendererConfig( preRenderEntry, postRenderEntry, textureLocationProvider, + alphaFunction, scaleHeight, scaleWidth ); this.deathMaxRotationProvider = deathMaxRotationProvider; + this.shadowRadius = shadowRadius; } public float getDeathMaxRotation(T entity) { return deathMaxRotationProvider.apply(entity); } + public float shadowRadius(T entity) { + return shadowRadius.apply(entity); + } + public static Builder builder( ResourceLocation modelLocation, ResourceLocation textureLocation @@ -71,12 +82,15 @@ public static class Builder extends AzRendererConfig.Builder deathMaxRotationProvider; + protected Function shadowRadius; + protected Builder( Function modelLocationProvider, Function textureLocationProvider ) { super(modelLocationProvider, textureLocationProvider); this.deathMaxRotationProvider = $ -> 90F; + this.shadowRadius = $ -> 0.0F; } @Override @@ -96,16 +110,16 @@ public Builder setRenderType(Function renderTypeProvider) { @Override public Builder setPrerenderEntry( - Function, AzRendererPipelineContext> preRenderEntry + UnaryOperator> preRenderEntry ) { - return (Builder) super.setPrerenderEntry(preRenderEntry); + return (AzEntityRendererConfig.Builder) super.setPrerenderEntry(preRenderEntry); } @Override - public Builder setPostRenderEntry( - Function, AzRendererPipelineContext> preRenderEntry + public Builder setPostRenderEntry( + UnaryOperator> preRenderEntry ) { - return (Builder) super.setPostRenderEntry(preRenderEntry); + return (AzEntityRendererConfig.Builder) super.setPostRenderEntry(preRenderEntry); } @Override @@ -113,6 +127,36 @@ public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> animator return (Builder) super.setAnimatorProvider(animatorProvider); } + @Override + public Builder setAlpha(Function alphaFunction) { + return (AzEntityRendererConfig.Builder) super.setAlpha(alphaFunction); + } + + @Override + public Builder setAlpha(float alpha) { + return (AzEntityRendererConfig.Builder) super.setAlpha(alpha); + } + + @Override + public Builder setScale(Function scaleFunction) { + return (AzEntityRendererConfig.Builder) super.setScale(scaleFunction); + } + + @Override + public Builder setScale(Function scaleHeightFunction, Function scaleWidthFunction) { + return (AzEntityRendererConfig.Builder) super.setScale(scaleHeightFunction, scaleWidthFunction); + } + + @Override + public Builder setScale(float scale) { + return (AzEntityRendererConfig.Builder) super.setScale(scale); + } + + @Override + public Builder setScale(float scaleWidth, float scaleHeight) { + return (AzEntityRendererConfig.Builder) super.setScale(scaleWidth, scaleHeight); + } + public Builder setDeathMaxRotation(float angle) { this.deathMaxRotationProvider = $ -> angle; return this; @@ -122,13 +166,38 @@ public Builder setDeathMaxRotation(float angle) { * Sets a provider for the max rotation value for dying entities.
    * You might want to modify this for different aesthetics, such as a * {@link net.minecraft.world.entity.monster.Spider} flipping upside down on death.
    - * Functionally equivalent to {@link net.minecraft.client.renderer.entity.LivingEntityRenderer#getFlipDegrees} */ public Builder setDeathMaxRotation(Function deathMaxRotationProvider) { this.deathMaxRotationProvider = deathMaxRotationProvider; return this; } + /** + * Sets a provider function for the shadow radius of an entity. The shadow radius determines the size of the + * shadow cast by the entity when rendered. This can be dynamic based on the entity's state. + * + * @param shadowRadiusFunction A function that provides the shadow radius value based on the entity. The + * function should return a Float representing the desired shadow radius. + * @return The current {@code Builder} instance with the shadow radius provider function set, allowing for + * method chaining. + */ + public Builder setShadowRadius(Function shadowRadiusFunction) { + this.shadowRadius = shadowRadiusFunction; + return this; + } + + /** + * Sets the shadow radius for the builder configuration. This value determines the size of the shadow rendered + * beneath the entity model. + * + * @param shadowRadius the radius of the shadow for the entity + * @return the current instance of the builder for chaining additional configurations + */ + public Builder setShadowRadius(float shadowRadius) { + this.shadowRadius = $ -> shadowRadius; + return this; + } + @Override public AzEntityRendererConfig build() { var baseConfig = super.build(); @@ -136,14 +205,16 @@ public AzEntityRendererConfig build() { return new AzEntityRendererConfig<>( baseConfig::createAnimator, deathMaxRotationProvider, + shadowRadius, baseConfig::modelLocation, baseConfig::getRenderType, baseConfig.renderLayers(), baseConfig::preRenderEntry, baseConfig::postRenderEntry, baseConfig::textureLocation, - baseConfig.scaleHeight(), - baseConfig.scaleWidth() + baseConfig::alpha, + baseConfig::scaleHeight, + baseConfig::scaleWidth ); } } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java index a0734b850..d19627ed9 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java @@ -66,10 +66,13 @@ public void preRender(AzRendererPipelineContext context, boolean isReRender) this.entityRenderTranslations.set(poseStack.last().pose()); var config = entityRenderer.config(); - var scaleWidth = config.scaleWidth(); - var scaleHeight = config.scaleHeight(); + var scaleWidth = config.scaleWidth(context.animatable()); + var scaleHeight = config.scaleHeight(context.animatable()); scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); + if (config.alpha(context.animatable()) < 1) { + context.setAlpha(config.alpha(context.animatable())); + } config.preRenderEntry(context); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java index d61e9c62b..052dbf8ec 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.function.Function; import java.util.function.Supplier; +import java.util.function.UnaryOperator; /** * Configuration class for rendering items using customized settings in an animation framework. Extends @@ -29,11 +30,12 @@ private AzItemRendererConfig( Function modelLocationProvider, Function renderTypeProvider, List> renderLayers, - Function, AzRendererPipelineContext> preRenderEntry, - Function, AzRendererPipelineContext> postRenderEntry, + UnaryOperator> preRenderEntry, + UnaryOperator> postRenderEntry, Function textureLocationProvider, - float scaleHeight, - float scaleWidth, + Function alphaFunction, + Function scaleHeight, + Function scaleWidth, boolean useEntityGuiLighting, boolean useNewOffset ) { @@ -45,6 +47,7 @@ private AzItemRendererConfig( preRenderEntry, postRenderEntry, textureLocationProvider, + alphaFunction, scaleHeight, scaleWidth ); @@ -105,17 +108,15 @@ public Builder setRenderType(Function renderTypeProvider) } @Override - public Builder setPrerenderEntry( - Function, AzRendererPipelineContext> preRenderEntry + public Builder setPrerenderEntry(UnaryOperator> preRenderEntry ) { - return (Builder) super.setPrerenderEntry(preRenderEntry); + return (AzItemRendererConfig.Builder) super.setPrerenderEntry(preRenderEntry); } @Override - public Builder setPostRenderEntry( - Function, AzRendererPipelineContext> preRenderEntry + public Builder setPostRenderEntry(UnaryOperator> preRenderEntry ) { - return (Builder) super.setPostRenderEntry(preRenderEntry); + return (AzItemRendererConfig.Builder) super.setPostRenderEntry(preRenderEntry); } @Override @@ -123,6 +124,39 @@ public Builder setAnimatorProvider(Supplier<@Nullable AzAnimator> ani return (Builder) super.setAnimatorProvider(animatorProvider); } + @Override + public Builder setAlpha(Function alphaFunction) { + return (AzItemRendererConfig.Builder) super.setAlpha(alphaFunction); + } + + @Override + public Builder setAlpha(float alpha) { + return (AzItemRendererConfig.Builder) super.setAlpha(alpha); + } + + @Override + public Builder setScale(Function scaleFunction) { + return (AzItemRendererConfig.Builder) super.setScale(scaleFunction); + } + + @Override + public Builder setScale( + Function scaleHeightFunction, + Function scaleWidthFunction + ) { + return (AzItemRendererConfig.Builder) super.setScale(scaleHeightFunction, scaleWidthFunction); + } + + @Override + public Builder setScale(float scale) { + return (AzItemRendererConfig.Builder) super.setScale(scale); + } + + @Override + public Builder setScale(float scaleWidth, float scaleHeight) { + return (AzItemRendererConfig.Builder) super.setScale(scaleWidth, scaleHeight); + } + public Builder useEntityGuiLighting() { this.useEntityGuiLighting = true; return this; @@ -149,8 +183,9 @@ public AzItemRendererConfig build() { baseConfig::preRenderEntry, baseConfig::postRenderEntry, baseConfig::textureLocation, - baseConfig.scaleHeight(), - baseConfig.scaleWidth(), + baseConfig::alpha, + baseConfig::scaleHeight, + baseConfig::scaleWidth, useEntityGuiLighting, useNewOffset ); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java index b6b49e174..297c66576 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java @@ -50,18 +50,23 @@ protected AzLayerRenderer createLayerRenderer(AzRendererConfig context, boolean isReRender) { + var itemContext = (AzItemRendererPipelineContext) context; var poseStack = context.poseStack(); this.itemRenderTranslations = new Matrix4f(poseStack.last().pose()); var config = itemRenderer.config(); - var scaleWidth = config.scaleWidth(); - var scaleHeight = config.scaleHeight(); + var scaleWidth = config.scaleWidth(context.animatable()); + var scaleHeight = config.scaleHeight(context.animatable()); scaleModelForRender(context, scaleWidth, scaleHeight, isReRender); if (!isReRender) { var useNewOffset = config.useNewOffset(); poseStack.translate(0.5f, useNewOffset ? 0.0f : 0.51f, 0.5f); } + if (config.alpha(context.animatable()) < 1) { + itemContext.setAlpha(config.alpha(context.animatable())); + itemContext.setTranslucent(true); + } config.preRenderEntry(context); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipelineContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipelineContext.java index 2d418134e..da79a5300 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipelineContext.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipelineContext.java @@ -18,11 +18,23 @@ */ public class AzItemRendererPipelineContext extends AzRendererPipelineContext { + private boolean translucent = false; + public AzItemRendererPipelineContext(AzRendererPipeline rendererPipeline) { super(rendererPipeline); } - // TODO: This is what Geckolib does, but it feels wrong to have this render type getter for an ITEM... + /** + * Sets whether the rendering pipeline should render with a translucent effect or not. + * + * @param translucent A boolean value indicating whether to enable or disable translucency. If true, the rendering + * pipeline will apply a translucent effect to rendered elements. If false, it will render with + * an opaque effect. + */ + public void setTranslucent(boolean translucent) { + this.translucent = translucent; + } + @Override public @NotNull RenderType getDefaultRenderType( ItemStack animatable, @@ -30,6 +42,8 @@ public AzItemRendererPipelineContext(AzRendererPipeline rendererPipel @Nullable MultiBufferSource bufferSource, float partialTick ) { - return RenderType.entityCutoutNoCull(texture); + return translucent + ? RenderType.itemEntityTranslucentCull(texture) + : RenderType.entityCutoutNoCull(texture); } } diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java index 64322520b..fd558ea8c 100644 --- a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java +++ b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java @@ -16,8 +16,8 @@ public class MarauderRenderer extends AzEntityRenderer { public MarauderRenderer(EntityRendererProvider.Context context) { super( AzEntityRendererConfig.builder(MODEL, TEXTURE) - .addRenderLayer(new AzAutoGlowingLayer<>()) .setAnimatorProvider(MarauderAnimator::new) + .addRenderLayer(new AzAutoGlowingLayer<>()) .setDeathMaxRotation(0F) .build(), context From 07569a80d770869ec7322bbce4d67d7530a6053e Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Thu, 15 May 2025 12:24:31 -0400 Subject: [PATCH 36/40] IT'S FINALLY DONE --- .../main/java/mod/azure/azurelib/cache/AzureLibCache.java | 6 ++++++ .../java/mod/azure/azurelib/core/animation/Animation.java | 1 - .../mod/azure/azurelib/core/keyframe/AnimationPoint.java | 1 - .../mod/azure/azurelib/core/keyframe/BoneAnimation.java | 1 - .../java/mod/azure/azurelib/core/keyframe/Keyframe.java | 1 - .../mod/azure/azurelib/core/keyframe/KeyframeLocation.java | 3 --- .../mod/azure/azurelib/core/keyframe/KeyframeStack.java | 1 - .../keyframe/event/data/CustomInstructionKeyframeData.java | 1 - .../azurelib/core/keyframe/event/data/KeyFrameData.java | 1 - .../core/keyframe/event/data/ParticleKeyframeData.java | 1 - .../core/keyframe/event/data/SoundKeyframeData.java | 1 - .../main/java/mod/azure/azurelib/loading/FileLoader.java | 2 -- .../azurelib/rewrite/animation/primitive/AzLoopType.java | 3 --- .../rewrite/animation/primitive/AzQueuedAnimation.java | 3 --- common/src/main/java/mod/azure/azurelib/util/JsonUtil.java | 6 ++++++ 15 files changed, 12 insertions(+), 20 deletions(-) diff --git a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java index 86e1f2772..9f0550184 100644 --- a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java +++ b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java @@ -89,12 +89,18 @@ public static CompletableFuture reload(PreparationBarrier stage, ResourceM }, gameExecutor); } + /** + * @deprecated + */ private static CompletableFuture loadAnimations(Executor backgroundExecutor, ResourceManager resourceManager, BiConsumer elementConsumer) { return loadResources(backgroundExecutor, resourceManager, "animations", resource -> FileLoader.loadAnimationsFile(resource, resourceManager), elementConsumer); } + /** + * @deprecated + */ private static CompletableFuture loadModels(Executor backgroundExecutor, ResourceManager resourceManager, BiConsumer elementConsumer) { return loadResources(backgroundExecutor, resourceManager, "geo", resource -> { diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/Animation.java b/common/src/main/java/mod/azure/azurelib/core/animation/Animation.java index d8adaae12..fae942533 100644 --- a/common/src/main/java/mod/azure/azurelib/core/animation/Animation.java +++ b/common/src/main/java/mod/azure/azurelib/core/animation/Animation.java @@ -28,7 +28,6 @@ * A compiled animation instance for use by the {@link AnimationController}
    * Modifications or extensions of a compiled Animation are not supported, and therefore an instance of Animation is considered final and immutable. */ -@Deprecated(forRemoval = true) public record Animation(String name, double length, LoopType loopType, BoneAnimation[] boneAnimations, Keyframes keyFrames) { public record Keyframes(SoundKeyframeData[] sounds, ParticleKeyframeData[] particles, CustomInstructionKeyframeData[] customInstructions) {} diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPoint.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPoint.java index fbf5a1195..e7e0b72f6 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPoint.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPoint.java @@ -20,7 +20,6 @@ * @param animationEndValue The end value to provide to the animation handling system * @param keyFrame The {@code Nullable} Keyframe */ -@Deprecated(forRemoval = true) public record AnimationPoint(Keyframe keyFrame, double currentTick, double transitionLength, double animationStartValue, double animationEndValue) { @Override public String toString() { diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimation.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimation.java index e4b1684bf..a82f648bf 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimation.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimation.java @@ -23,7 +23,6 @@ * @param positionKeyFrames The deserialized position {@code Keyframe} stack * @param scaleKeyFrames The deserialized scale {@code Keyframe} stack */ -@Deprecated(forRemoval = true) public record BoneAnimation(String boneName, KeyframeStack> rotationKeyFrames, KeyframeStack> positionKeyFrames, diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/Keyframe.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/Keyframe.java index 27d036f33..721ffc3cb 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/Keyframe.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/Keyframe.java @@ -27,7 +27,6 @@ * @param easingType The {@code EasingType} to use for transformations * @param easingArgs The arguments to provide to the easing calculation */ -@Deprecated(forRemoval = true) public record Keyframe(double length, T startValue, T endValue, EasingType easingType, List easingArgs) { public Keyframe(double length, T startValue, T endValue) { this(length, startValue, endValue, EasingType.LINEAR); diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java index 2a8827357..82f594804 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java @@ -12,13 +12,10 @@ package mod.azure.azurelib.core.keyframe; -import mod.azure.azurelib.core.math.IValue; - /** * A named pair object that stores a {@link Keyframe} and a double representing a temporally placed {@code Keyframe} * @param keyframe The {@code Keyframe} at the tick time * @param startTick The animation tick time at the start of this {@code Keyframe} */ -@Deprecated(forRemoval = true) public record KeyframeLocation>(T keyframe, double startTick) { } //TODO: public record KeyframeLocation(Keyframe keyframe, double startTick) { } \ No newline at end of file diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeStack.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeStack.java index 34a2df5f0..e594bacc0 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeStack.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeStack.java @@ -19,7 +19,6 @@ /** * Stores a triplet of {@link Keyframe Keyframes} in an ordered stack */ -@Deprecated(forRemoval = true) public record KeyframeStack>(List xKeyframes, List yKeyframes, List zKeyframes) { public KeyframeStack() { this(new ObjectArrayList<>(), new ObjectArrayList<>(), new ObjectArrayList<>()); diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/CustomInstructionKeyframeData.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/CustomInstructionKeyframeData.java index 42a64e126..6a4ff4597 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/CustomInstructionKeyframeData.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/CustomInstructionKeyframeData.java @@ -14,7 +14,6 @@ /** * Custom instruction {@link Keyframe} instruction holder */ -@Deprecated(forRemoval = true) public class CustomInstructionKeyframeData extends KeyFrameData { private final String instructions; diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/KeyFrameData.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/KeyFrameData.java index 1c83c7660..0f9aa2ec9 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/KeyFrameData.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/KeyFrameData.java @@ -21,7 +21,6 @@ * @see ParticleKeyframeData * @see SoundKeyframeData */ -@Deprecated(forRemoval = true) public abstract class KeyFrameData { private final double startTick; diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/ParticleKeyframeData.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/ParticleKeyframeData.java index b4f6c62bc..80910cad1 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/ParticleKeyframeData.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/ParticleKeyframeData.java @@ -14,7 +14,6 @@ /** * Particle {@link Keyframe} instruction holder */ -@Deprecated(forRemoval = true) public class ParticleKeyframeData extends KeyFrameData { private final String effect; private final String locator; diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/SoundKeyframeData.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/SoundKeyframeData.java index 4bc7ac674..a2638958f 100644 --- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/SoundKeyframeData.java +++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/data/SoundKeyframeData.java @@ -14,7 +14,6 @@ /** * Sound {@link Keyframe} instruction holder */ -@Deprecated(forRemoval = true) public class SoundKeyframeData extends KeyFrameData { private final String sound; diff --git a/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java b/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java index e4b4936ea..66f825ffe 100644 --- a/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java +++ b/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java @@ -34,7 +34,6 @@ public final class FileLoader { * @param location The resource path of the animations file * @param manager The Minecraft {@code ResourceManager} responsible for maintaining in-memory resource access */ - @Deprecated(forRemoval = true) public static BakedAnimations loadAnimationsFile(ResourceLocation location, ResourceManager manager) { return JsonUtil.GEO_GSON.fromJson(loadFile(location, manager), BakedAnimations.class); } @@ -55,7 +54,6 @@ public static AzBakedAnimations loadAzAnimationsFile(ResourceLocation location, * @param location The resource path of the model file * @param manager The Minecraft {@code ResourceManager} responsible for maintaining in-memory resource access */ - @Deprecated(forRemoval = true) public static Model loadModelFile(ResourceLocation location, ResourceManager manager) { return JsonUtil.GEO_GSON.fromJson(loadFile(location, manager), Model.class); } diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzLoopType.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzLoopType.java index 5f49ed224..2c55477e9 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzLoopType.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzLoopType.java @@ -11,10 +11,7 @@ * Loop type functional interface to define post-play handling for a given animation.
    * Custom loop types are supported by extending this class and providing the extended class instance as the loop type * for the animation - * - * @deprecated */ -@Deprecated(forRemoval = true) public interface AzLoopType { String name(); diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzQueuedAnimation.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzQueuedAnimation.java index 436c92450..7d01baf03 100644 --- a/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzQueuedAnimation.java +++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzQueuedAnimation.java @@ -17,10 +17,7 @@ *

  • {@code playBehavior}: The {@link AzPlayBehavior} that dictates the looping behavior or termination handling for the * animation.
  • * - * - * @deprecated */ -@Deprecated(forRemoval = true) public record AzQueuedAnimation( AzBakedAnimation animation, AzPlayBehavior playBehavior diff --git a/common/src/main/java/mod/azure/azurelib/util/JsonUtil.java b/common/src/main/java/mod/azure/azurelib/util/JsonUtil.java index 0cbd28e77..a05ea26cc 100644 --- a/common/src/main/java/mod/azure/azurelib/util/JsonUtil.java +++ b/common/src/main/java/mod/azure/azurelib/util/JsonUtil.java @@ -12,6 +12,10 @@ import java.util.Map; import java.util.function.Function; +import mod.azure.azurelib.rewrite.animation.parse.AzBakedAnimationsAdapter; +import mod.azure.azurelib.rewrite.animation.parse.AzKeyframesAdapter; +import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimations; +import mod.azure.azurelib.rewrite.animation.primitive.AzKeyframes; import org.jetbrains.annotations.Nullable; import com.google.gson.Gson; @@ -63,6 +67,8 @@ public final class JsonUtil { .registerTypeAdapter(UVUnion.class, UVUnion.deserializer()) .registerTypeAdapter(Animation.Keyframes.class, new KeyFramesAdapter()) .registerTypeAdapter(BakedAnimations.class, new BakedAnimationsAdapter()) + .registerTypeAdapter(AzKeyframes.class, new AzKeyframesAdapter()) + .registerTypeAdapter(AzBakedAnimations.class, new AzBakedAnimationsAdapter()) .create(); /** From 3a8c0bf280d0c835c2d2b4ae878be845061a15f6 Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Thu, 15 May 2025 13:19:18 -0400 Subject: [PATCH 37/40] Update AzureLib.java --- common/src/main/java/mod/azure/azurelib/AzureLib.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/mod/azure/azurelib/AzureLib.java b/common/src/main/java/mod/azure/azurelib/AzureLib.java index 03b100258..46e0247b1 100644 --- a/common/src/main/java/mod/azure/azurelib/AzureLib.java +++ b/common/src/main/java/mod/azure/azurelib/AzureLib.java @@ -28,7 +28,7 @@ public static void initialize() { hasInitialized = true; } - public static final ResourceLocation modResource(String name) { + public static ResourceLocation modResource(String name) { return new ResourceLocation(MOD_ID, name); } } From 9a300444756dbb608b1fa2bb594b87dad7bb68ea Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Thu, 15 May 2025 22:24:33 -0400 Subject: [PATCH 38/40] Remove examples --- .../azure/azurelib/testing/CommonStrings.java | 27 - .../testing/armor/DoomArmorBoneProvider.java | 28 - .../testing/armor/DoomicornArmor.java | 40 - .../DoomicornArmorAnimationDispatcher.java | 18 - .../testing/armor/DoomicornArmorAnimator.java | 30 - .../testing/armor/DoomicornArmorRenderer.java | 24 - .../entity/MarauderAnimationDispatcher.java | 81 - .../testing/entity/MarauderAnimator.java | 108 - .../testing/entity/MarauderEntity.java | 103 - .../testing/entity/MarauderRenderer.java | 26 - .../entity/ai/DelayedMeleeAttackGoal.java | 179 - .../item/PistolAnimationDispatcher.java | 20 - .../azurelib/testing/item/PistolAnimator.java | 34 - .../azurelib/testing/item/PistolItem.java | 45 - .../azurelib/testing/item/PistolRenderer.java | 21 - .../animations/block/stargate.animation.json | 249 - .../animations/entity/marauder.animation.json | 6262 ------ .../animations/item/doomicorn.animation.json | 741 - .../animations/item/pistol.animation.json | 80 - .../azurelib/geo/block/stargate.geo.json | 877 - .../azurelib/geo/entity/marauder.geo.json | 4168 ---- .../azurelib/geo/item/doomicorn.geo.json | 17113 ---------------- .../assets/azurelib/geo/item/pistol.geo.json | 3690 ---- .../azurelib/models/block/stargate.json | 99 - .../azurelib/models/item/doomicorn_boots.json | 6 - .../models/item/doomicorn_chestplate.json | 6 - .../models/item/doomicorn_helmet.json | 6 - .../models/item/doomicorn_leggings.json | 6 - .../assets/azurelib/models/item/pistol.json | 96 - .../assets/azurelib/models/item/stargate.json | 96 - .../azurelib/textures/block/stargate.png | Bin 8667 -> 0 bytes .../azurelib/textures/entity/marauder.png | Bin 11573 -> 0 bytes .../textures/entity/marauder_glowmask.png | Bin 8489 -> 0 bytes .../azurelib/textures/item/doomicorn.png | Bin 1189 -> 0 bytes .../textures/item/doomicorn_boots.png | Bin 136 -> 0 bytes .../textures/item/doomicorn_chestplate.png | Bin 338 -> 0 bytes .../textures/item/doomicorn_helmet.png | Bin 503 -> 0 bytes .../textures/item/doomicorn_leggings.png | Bin 139 -> 0 bytes .../assets/azurelib/textures/item/pistol.png | Bin 1486 -> 0 bytes .../mod/azure/azurelib/ClientListener.java | 26 - .../mod/azure/azurelib/FabricAzureLibMod.java | 40 - .../azurelib/testing/block/StargateBlock.java | 53 - .../be/StargateBlockAnimationDispatcher.java | 26 - .../testing/block/be/StargateBlockEntity.java | 32 - .../block/be/StargateBlockEntityAnimator.java | 38 - .../block/be/StargateBlockRenderer.java | 21 - .../mod/azure/azurelib/ClientModListener.java | 29 - .../azure/azurelib/NeoForgeAzureLibMod.java | 72 - .../azurelib/testing/block/StargateBlock.java | 57 - .../be/StargateBlockAnimationDispatcher.java | 26 - .../testing/block/be/StargateBlockEntity.java | 33 - .../block/be/StargateBlockEntityAnimator.java | 39 - .../block/be/StargateBlockRenderer.java | 22 - 53 files changed, 34793 deletions(-) delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/CommonStrings.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomArmorBoneProvider.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmor.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimationDispatcher.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimator.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorRenderer.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimationDispatcher.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimator.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/MarauderEntity.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/entity/ai/DelayedMeleeAttackGoal.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimationDispatcher.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimator.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java delete mode 100644 common/src/main/java/mod/azure/azurelib/testing/item/PistolRenderer.java delete mode 100644 common/src/main/resources/assets/azurelib/animations/block/stargate.animation.json delete mode 100644 common/src/main/resources/assets/azurelib/animations/entity/marauder.animation.json delete mode 100644 common/src/main/resources/assets/azurelib/animations/item/doomicorn.animation.json delete mode 100644 common/src/main/resources/assets/azurelib/animations/item/pistol.animation.json delete mode 100644 common/src/main/resources/assets/azurelib/geo/block/stargate.geo.json delete mode 100644 common/src/main/resources/assets/azurelib/geo/entity/marauder.geo.json delete mode 100644 common/src/main/resources/assets/azurelib/geo/item/doomicorn.geo.json delete mode 100644 common/src/main/resources/assets/azurelib/geo/item/pistol.geo.json delete mode 100644 common/src/main/resources/assets/azurelib/models/block/stargate.json delete mode 100644 common/src/main/resources/assets/azurelib/models/item/doomicorn_boots.json delete mode 100644 common/src/main/resources/assets/azurelib/models/item/doomicorn_chestplate.json delete mode 100644 common/src/main/resources/assets/azurelib/models/item/doomicorn_helmet.json delete mode 100644 common/src/main/resources/assets/azurelib/models/item/doomicorn_leggings.json delete mode 100644 common/src/main/resources/assets/azurelib/models/item/pistol.json delete mode 100644 common/src/main/resources/assets/azurelib/models/item/stargate.json delete mode 100644 common/src/main/resources/assets/azurelib/textures/block/stargate.png delete mode 100644 common/src/main/resources/assets/azurelib/textures/entity/marauder.png delete mode 100644 common/src/main/resources/assets/azurelib/textures/entity/marauder_glowmask.png delete mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn.png delete mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn_boots.png delete mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn_chestplate.png delete mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn_helmet.png delete mode 100644 common/src/main/resources/assets/azurelib/textures/item/doomicorn_leggings.png delete mode 100644 common/src/main/resources/assets/azurelib/textures/item/pistol.png delete mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java delete mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java delete mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java delete mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java delete mode 100644 fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java delete mode 100644 neo/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java delete mode 100644 neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java delete mode 100644 neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java delete mode 100644 neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java delete mode 100644 neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java diff --git a/common/src/main/java/mod/azure/azurelib/testing/CommonStrings.java b/common/src/main/java/mod/azure/azurelib/testing/CommonStrings.java deleted file mode 100644 index 39de52fe2..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/CommonStrings.java +++ /dev/null @@ -1,27 +0,0 @@ -package mod.azure.azurelib.testing; - -public record CommonStrings() { - public static final String MOD_ID = "azexamples"; - - public static final String CREATIVE_TAB = "itemGroup." + CommonStrings.MOD_ID + ".examplemod_items"; - - public static final String BASE_CONTROLLER = "base_controller"; - - public static final String IDLE_ANIMATION_NAME = "idle"; - - public static final String WALK_ANIMATION_NAME = "walk"; - - public static final String SPAWN_ANIMATION_NAME = "spawn"; - - public static final String DEATH_ANIMATION_NAME = "death"; - - public static final String RUN_ANIMATION_NAME = "run"; - - public static final String MELEE_ANIMATION_NAME = "axe_attack"; - - public static final String SPIN_ANIMATION_NAME = "spinning"; - - public static final String FIRING_ANIMATION_NAME = "firing"; - - public static final String EQUIP_ANIMATION_NAME = "equipping"; -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomArmorBoneProvider.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomArmorBoneProvider.java deleted file mode 100644 index 4a94f0af7..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomArmorBoneProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -package mod.azure.azurelib.testing.armor; - -import mod.azure.azurelib.rewrite.model.AzBakedModel; -import mod.azure.azurelib.rewrite.model.AzBone; -import mod.azure.azurelib.rewrite.render.armor.bone.AzDefaultArmorBoneProvider; - -public class DoomArmorBoneProvider extends AzDefaultArmorBoneProvider { - - @Override - public AzBone getLeftBootBone(AzBakedModel model) { - return model.getBone("armorRightBoot").orElse(null); - } - - @Override - public AzBone getLeftLegBone(AzBakedModel model) { - return model.getBone("armorRightLeg").orElse(null); - } - - @Override - public AzBone getRightBootBone(AzBakedModel model) { - return model.getBone("armorLeftBoot").orElse(null); - } - - @Override - public AzBone getRightLegBone(AzBakedModel model) { - return model.getBone("armorLeftLeg").orElse(null); - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmor.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmor.java deleted file mode 100644 index b9a9f6f6b..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmor.java +++ /dev/null @@ -1,40 +0,0 @@ -package mod.azure.azurelib.testing.armor; - -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResultHolder; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.ArmorMaterials; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import org.jetbrains.annotations.NotNull; - -public class DoomicornArmor extends ArmorItem { - - private final DoomicornArmorAnimationDispatcher dispatcher; - - public DoomicornArmor(Type type) { - super(ArmorMaterials.NETHERITE, type, new Properties().stacksTo(1)); - this.dispatcher = new DoomicornArmorAnimationDispatcher(); - } - - @Override - public @NotNull InteractionResultHolder swapWithEquipmentSlot( - @NotNull Item item, - @NotNull Level level, - @NotNull Player player, - @NotNull InteractionHand hand - ) { - InteractionResultHolder result = super.swapWithEquipmentSlot(item, level, player, hand); - - if (!level.isClientSide) { - EquipmentSlot slot = getEquipmentSlot(); - ItemStack itemStack = player.getItemBySlot(slot); - dispatcher.serverEquipHelmet(player, itemStack); - } - - return result; - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimationDispatcher.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimationDispatcher.java deleted file mode 100644 index 208c99432..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimationDispatcher.java +++ /dev/null @@ -1,18 +0,0 @@ -package mod.azure.azurelib.testing.armor; - -import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; -import mod.azure.azurelib.testing.CommonStrings; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.item.ItemStack; - -public class DoomicornArmorAnimationDispatcher { - - private static final AzCommand EQUIP = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.EQUIP_ANIMATION_NAME - ); - - public void serverEquipHelmet(Entity entity, ItemStack itemStack) { - EQUIP.sendForItem(entity, itemStack); - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimator.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimator.java deleted file mode 100644 index f8e013b64..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorAnimator.java +++ /dev/null @@ -1,30 +0,0 @@ -package mod.azure.azurelib.testing.armor; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; -import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; -import mod.azure.azurelib.testing.CommonStrings; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.NotNull; - -public class DoomicornArmorAnimator extends AzItemAnimator { - - private static final ResourceLocation ANIMATIONS = AzureLib.modResource( - "animations/item/doomicorn.animation.json" - ); - - @Override - public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { - animationControllerContainer.add( - AzAnimationController.builder(this, CommonStrings.BASE_CONTROLLER) - .build() - ); - } - - @Override - public @NotNull ResourceLocation getAnimationLocation(ItemStack animatable) { - return ANIMATIONS; - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorRenderer.java b/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorRenderer.java deleted file mode 100644 index 62acec796..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/armor/DoomicornArmorRenderer.java +++ /dev/null @@ -1,24 +0,0 @@ -package mod.azure.azurelib.testing.armor; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.render.armor.AzArmorRenderer; -import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererConfig; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.resources.ResourceLocation; - -public class DoomicornArmorRenderer extends AzArmorRenderer { - - private static final ResourceLocation MODEL = AzureLib.modResource("geo/item/doomicorn.geo.json"); - - private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/item/doomicorn.png"); - - public DoomicornArmorRenderer() { - super( - AzArmorRendererConfig.builder(MODEL, TEXTURE) - .setAnimatorProvider(DoomicornArmorAnimator::new) - .setBoneProvider(new DoomArmorBoneProvider()) - .setRenderType(RenderType.entityTranslucent(TEXTURE)) - .build() - ); - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimationDispatcher.java b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimationDispatcher.java deleted file mode 100644 index 019d7fbb1..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimationDispatcher.java +++ /dev/null @@ -1,81 +0,0 @@ -package mod.azure.azurelib.testing.entity; - -import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; -import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviors; -import mod.azure.azurelib.testing.CommonStrings; - -/** - * The MarauderAnimationDispatcher class is responsible for managing and dispatching animation commands for entities, - * specifically tailored for the Marauder entity. It uses predefined animation commands to sync animations with specific - * entity actions such as idling, walking, running, melee attacking, spawning, or dying. This dispatcher operates - * independently, providing a clean and centralized way to handle animation transitions, triggered by client or server - * events. - */ -public class MarauderAnimationDispatcher { - - private final AzCommand deathCommand = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.DEATH_ANIMATION_NAME, - AzPlayBehaviors.HOLD_ON_LAST_FRAME - ); - - private final AzCommand idleCommand = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.IDLE_ANIMATION_NAME, - AzPlayBehaviors.LOOP - ); - - private final AzCommand walkCommand = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.WALK_ANIMATION_NAME, - AzPlayBehaviors.LOOP - ); - - private final AzCommand runCommand = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.RUN_ANIMATION_NAME, - AzPlayBehaviors.LOOP - ); - - private final AzCommand meleeCommand = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.MELEE_ANIMATION_NAME, - AzPlayBehaviors.PLAY_ONCE - ); - - private final AzCommand spawnCommand = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.SPAWN_ANIMATION_NAME, - AzPlayBehaviors.PLAY_ONCE - ); - - private final MarauderEntity marauder; - - public MarauderAnimationDispatcher(MarauderEntity marauder) { - this.marauder = marauder; - } - - public void clientIdle() { - idleCommand.sendForEntity(marauder); - } - - public void clientWalk() { - walkCommand.sendForEntity(marauder); - } - - public void clientRun() { - runCommand.sendForEntity(marauder); - } - - public void serverMelee() { - meleeCommand.sendForEntity(marauder); - } - - public void clientDeath() { - deathCommand.sendForEntity(marauder); - } - - public void clientSpawn() { - spawnCommand.sendForEntity(marauder); - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimator.java b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimator.java deleted file mode 100644 index d0f1f6145..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderAnimator.java +++ /dev/null @@ -1,108 +0,0 @@ -package mod.azure.azurelib.testing.entity; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; -import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeCallbacks; -import mod.azure.azurelib.rewrite.animation.impl.AzEntityAnimator; -import mod.azure.azurelib.testing.CommonStrings; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import org.jetbrains.annotations.NotNull; - -/** - * The {@code MarauderAnimator} class is responsible for controlling the animations of a {@link MarauderEntity}. It - * defines the animation workflows for various states such as idle, walking, running, spawning, attacking, and dying, - * and binds these animations to the corresponding keyframe events. This class extends the {@code AzEntityAnimator} - * framework, providing an implementation specific to the {@code MarauderEntity}. - */ -public class MarauderAnimator extends AzEntityAnimator { - - private static final ResourceLocation ANIMATIONS = AzureLib.modResource( - "animations/entity/marauder.animation.json" - ); - - public MarauderAnimator() { - super(AzAnimatorConfig.defaultConfig()); - } - - @Override - public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { - animationControllerContainer.add( - AzAnimationController.builder(this, CommonStrings.BASE_CONTROLLER) - .setTransitionLength(0) - .setKeyframeCallbacks( - AzKeyframeCallbacks.builder() - .setSoundKeyframeHandler( - event -> { - if (event.getKeyframeData().getSound().equals("walk")) { - event.getAnimatable() - .level() - .playLocalSound( - event.getAnimatable().getX(), - event.getAnimatable().getY(), - event.getAnimatable().getZ(), - SoundEvents.METAL_STEP, - SoundSource.HOSTILE, - 1.00F, - 1.0F, - true - ); - } - if (event.getKeyframeData().getSound().equals("run")) { - event.getAnimatable() - .level() - .playLocalSound( - event.getAnimatable().getX(), - event.getAnimatable().getY(), - event.getAnimatable().getZ(), - SoundEvents.SKELETON_STEP, - SoundSource.HOSTILE, - 1.00F, - 1.0F, - true - ); - } - if (event.getKeyframeData().getSound().equals("portal")) { - event.getAnimatable() - .level() - .playLocalSound( - event.getAnimatable().getX(), - event.getAnimatable().getY(), - event.getAnimatable().getZ(), - SoundEvents.PORTAL_AMBIENT, - SoundSource.HOSTILE, - 0.20F, - 1.0F, - true - ); - } - if (event.getKeyframeData().getSound().equals("axe")) { - event.getAnimatable() - .level() - .playLocalSound( - event.getAnimatable().getX(), - event.getAnimatable().getY(), - event.getAnimatable().getZ(), - SoundEvents.ENDER_EYE_LAUNCH, - SoundSource.HOSTILE, - 1.00F, - 1.0F, - true - ); - } - } - ) - .build() - ) - .build() - ); - } - - @Override - public @NotNull ResourceLocation getAnimationLocation(MarauderEntity drone) { - return ANIMATIONS; - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderEntity.java b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderEntity.java deleted file mode 100644 index 16f8b39e4..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderEntity.java +++ /dev/null @@ -1,103 +0,0 @@ -package mod.azure.azurelib.testing.entity; - -import mod.azure.azurelib.ai.pathing.AzureNavigation; -import mod.azure.azurelib.rewrite.util.MoveAnalysis; -import mod.azure.azurelib.testing.entity.ai.DelayedMeleeAttackGoal; -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.ai.goal.RandomStrollGoal; -import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; -import net.minecraft.world.entity.ai.navigation.PathNavigation; -import net.minecraft.world.entity.monster.Monster; -import net.minecraft.world.entity.npc.AbstractVillager; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.NotNull; - -public class MarauderEntity extends Monster { - - /** - * Handles the animation state transitions for the {@link MarauderEntity}. This dispatcher is responsible for - * deciding and applying the appropriate animations to the entity based on its current state and actions, such as - * walking, running, idling, spawning, attacking, or dying. This instance operates primarily on the client side to - * handle visual representation of the {@link MarauderEntity} and is updated within the entity's tick lifecycle. - */ - public final MarauderAnimationDispatcher animationDispatcher; - - private final MoveAnalysis moveAnalysis; - - public MarauderEntity(EntityType entityType, Level level) { - super(entityType, level); - this.animationDispatcher = new MarauderAnimationDispatcher(this); - this.moveAnalysis = new MoveAnalysis(this); - } - - @Override - protected @NotNull PathNavigation createNavigation(@NotNull Level level) { - return new AzureNavigation(this, level); - } - - @Override - public float maxUpStep() { - return 2.0F; - } - - @Override - protected void tickDeath() { - ++this.deathTime; - if (this.deathTime >= 80 && !this.level().isClientSide() && !this.isRemoved()) { - this.level().broadcastEntityEvent(this, (byte) 60); - this.remove(RemovalReason.KILLED); - } - } - - @Override - public void tick() { - super.tick(); - moveAnalysis.update(); - - if (this.level().isClientSide) { - var isMovingOnGround = moveAnalysis.isMovingHorizontally() && onGround(); - Runnable animationRunner; - if (!this.isAlive()) { - animationRunner = animationDispatcher::clientDeath; - // } else if (this.tickCount < 270) { - // animationDispatcher.clientSpawn(); - } else if (isMovingOnGround) { - if (this.isAggressive()) { - animationRunner = animationDispatcher::clientRun; - } else { - animationRunner = animationDispatcher::clientWalk; - } - } else { - animationRunner = animationDispatcher::clientIdle; - } - animationRunner.run(); - } else { - // if (this.tickCount < 280 && this.isAlive()) { - // if (this.getNavigation() instanceof AzureNavigation azureNavigation) { - // azureNavigation.hardStop(); - // azureNavigation.stop(); - // } - // this.setYBodyRot(0); - // this.setYHeadRot(0); - // this.getEyePosition(90); - // this.setXRot(0); - // this.setYRot(0); - // } - } - } - - /** - * TODO: Get longer Melee animations working - */ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(7, new RandomStrollGoal(this, 0.3F)); - this.goalSelector.addGoal(2, new DelayedMeleeAttackGoal(this, 0.6F, true)); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true)); - } - - @Override - protected void playStepSound(@NotNull BlockPos pos, @NotNull BlockState state) {} -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java b/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java deleted file mode 100644 index fd558ea8c..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/entity/MarauderRenderer.java +++ /dev/null @@ -1,26 +0,0 @@ -package mod.azure.azurelib.testing.entity; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.render.entity.AzEntityRenderer; -import mod.azure.azurelib.rewrite.render.entity.AzEntityRendererConfig; -import mod.azure.azurelib.rewrite.render.layer.AzAutoGlowingLayer; -import net.minecraft.client.renderer.entity.EntityRendererProvider; -import net.minecraft.resources.ResourceLocation; - -public class MarauderRenderer extends AzEntityRenderer { - - private static final ResourceLocation MODEL = AzureLib.modResource("geo/entity/marauder.geo.json"); - - private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/entity/marauder.png"); - - public MarauderRenderer(EntityRendererProvider.Context context) { - super( - AzEntityRendererConfig.builder(MODEL, TEXTURE) - .setAnimatorProvider(MarauderAnimator::new) - .addRenderLayer(new AzAutoGlowingLayer<>()) - .setDeathMaxRotation(0F) - .build(), - context - ); - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/entity/ai/DelayedMeleeAttackGoal.java b/common/src/main/java/mod/azure/azurelib/testing/entity/ai/DelayedMeleeAttackGoal.java deleted file mode 100644 index 638adfef9..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/entity/ai/DelayedMeleeAttackGoal.java +++ /dev/null @@ -1,179 +0,0 @@ -package mod.azure.azurelib.testing.entity.ai; - -import mod.azure.azurelib.testing.entity.MarauderEntity; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.EntitySelector; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.PathfinderMob; -import net.minecraft.world.entity.ai.goal.Goal; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.pathfinder.Path; - -import java.util.EnumSet; - -public class DelayedMeleeAttackGoal extends Goal { - - protected final PathfinderMob mob; - - private final double speedModifier; - - private final boolean followingTargetEvenIfNotSeen; - - public Path path; - - private double pathedTargetX; - - private double pathedTargetY; - - private double pathedTargetZ; - - private int ticksUntilNextPathRecalculation; - - private int ticksUntilNextAttack; - - public long lastCanUseCheck; - - private static final long COOLDOWN_BETWEEN_CAN_USE_CHECKS = 20L; - - private int delayCounter; - - public DelayedMeleeAttackGoal(PathfinderMob mob, double speedModifier, boolean followingTargetEvenIfNotSeen) { - this.mob = mob; - this.speedModifier = speedModifier; - this.followingTargetEvenIfNotSeen = followingTargetEvenIfNotSeen; - this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); - } - - public boolean canUse() { - long i = this.mob.level().getGameTime(); - if (i - this.lastCanUseCheck < 20L) { - return false; - } else { - this.lastCanUseCheck = i; - LivingEntity livingentity = this.mob.getTarget(); - if (livingentity == null) { - return false; - } else if (!livingentity.isAlive()) { - return false; - } else { - this.path = this.mob.getNavigation().createPath(livingentity, 0); - return this.path != null ? true : this.mob.isWithinMeleeAttackRange(livingentity); - } - } - } - - public boolean canContinueToUse() { - LivingEntity livingentity = this.mob.getTarget(); - if (livingentity == null) { - return false; - } else if (!livingentity.isAlive()) { - return false; - } else if (!this.followingTargetEvenIfNotSeen) { - return !this.mob.getNavigation().isDone(); - } else { - return !this.mob.isWithinRestriction(livingentity.blockPosition()) - ? false - : !(livingentity instanceof Player) || !livingentity.isSpectator() && !((Player) livingentity) - .isCreative(); - } - } - - public void start() { - this.mob.getNavigation().moveTo(this.path, this.speedModifier); - this.mob.setAggressive(true); - this.ticksUntilNextPathRecalculation = 0; - this.ticksUntilNextAttack = 0; - this.delayCounter = 0; - } - - public void stop() { - LivingEntity livingentity = this.mob.getTarget(); - if (!EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(livingentity)) { - this.mob.setTarget((LivingEntity) null); - } - - this.mob.setAggressive(false); - this.mob.getNavigation().stop(); - this.delayCounter = 0; - } - - public boolean requiresUpdateEveryTick() { - return true; - } - - public void tick() { - LivingEntity livingentity = this.mob.getTarget(); - if (livingentity != null) { - if (!this.mob.level().isClientSide()) { - this.delayCounter++; - } - this.mob.getLookControl().setLookAt(livingentity, 30.0F, 30.0F); - this.ticksUntilNextPathRecalculation = Math.max(this.ticksUntilNextPathRecalculation - 1, 0); - if ( - (this.followingTargetEvenIfNotSeen || this.mob.getSensing().hasLineOfSight(livingentity)) - && this.ticksUntilNextPathRecalculation <= 0 && (this.pathedTargetX == (double) 0.0F - && this.pathedTargetY == (double) 0.0F && this.pathedTargetZ == (double) 0.0F || livingentity - .distanceToSqr(this.pathedTargetX, this.pathedTargetY, this.pathedTargetZ) >= (double) 1.0F - || this.mob.getRandom().nextFloat() < 0.05F) - ) { - this.pathedTargetX = livingentity.getX(); - this.pathedTargetY = livingentity.getY(); - this.pathedTargetZ = livingentity.getZ(); - this.ticksUntilNextPathRecalculation = 4 + this.mob.getRandom().nextInt(7); - double d0 = this.mob.distanceToSqr(livingentity); - if (d0 > (double) 1024.0F) { - this.ticksUntilNextPathRecalculation += 10; - } else if (d0 > (double) 256.0F) { - this.ticksUntilNextPathRecalculation += 5; - } - - if (!this.mob.getNavigation().moveTo(livingentity, this.speedModifier)) { - this.ticksUntilNextPathRecalculation += 15; - } - - this.ticksUntilNextPathRecalculation = this.adjustedTickDelay(this.ticksUntilNextPathRecalculation); - } - - if ( - !this.mob.level().isClientSide() && this.mob.getTarget() != null && this.mob.isWithinMeleeAttackRange( - this.mob.getTarget() - ) - ) { - ((MarauderEntity) this.mob).animationDispatcher.serverMelee(); - } - this.ticksUntilNextAttack = Math.max(this.ticksUntilNextAttack - 1, 0); - this.checkAndPerformAttack(livingentity); - } - } - - protected void checkAndPerformAttack(LivingEntity target) { - if (this.canPerformAttack(target)) { - this.resetAttackCooldown(); - this.mob.swing(InteractionHand.MAIN_HAND); - this.mob.doHurtTarget(target); - } - } - - protected void resetAttackCooldown() { - this.ticksUntilNextAttack = this.adjustedTickDelay(80); - this.delayCounter = 0; - } - - protected boolean isTimeToAttack() { - return this.ticksUntilNextAttack <= 0; - } - - protected boolean canPerformAttack(LivingEntity entity) { - return this.delayCounter > 60 && this.isTimeToAttack() && this.mob.isWithinMeleeAttackRange(entity) && this.mob - .getSensing() - .hasLineOfSight(entity); - } - - protected int getTicksUntilNextAttack() { - return this.ticksUntilNextAttack; - } - - protected int getAttackInterval() { - return this.adjustedTickDelay(80); - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimationDispatcher.java b/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimationDispatcher.java deleted file mode 100644 index 0e64a0227..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimationDispatcher.java +++ /dev/null @@ -1,20 +0,0 @@ -package mod.azure.azurelib.testing.item; - -import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; -import mod.azure.azurelib.rewrite.animation.play_behavior.AzPlayBehaviors; -import mod.azure.azurelib.testing.CommonStrings; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.item.ItemStack; - -public class PistolAnimationDispatcher { - - private static final AzCommand FIRING_COMMAND = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.FIRING_ANIMATION_NAME, - AzPlayBehaviors.PLAY_ONCE - ); - - public void serverFire(Entity entity, ItemStack itemStack) { - FIRING_COMMAND.sendForItem(entity, itemStack); - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimator.java b/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimator.java deleted file mode 100644 index 2cf491539..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/item/PistolAnimator.java +++ /dev/null @@ -1,34 +0,0 @@ -package mod.azure.azurelib.testing.item; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; -import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator; -import mod.azure.azurelib.testing.CommonStrings; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.NotNull; - -public class PistolAnimator extends AzItemAnimator { - - private static final ResourceLocation ANIMATIONS = AzureLib.modResource("animations/item/pistol.animation.json"); - - public PistolAnimator() { - super(AzAnimatorConfig.defaultConfig()); - } - - @Override - public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { - animationControllerContainer.add( - AzAnimationController.builder(this, CommonStrings.BASE_CONTROLLER) - .build() - ); - } - - @Override - public @NotNull ResourceLocation getAnimationLocation(ItemStack animatable) { - return ANIMATIONS; - } - -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java b/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java deleted file mode 100644 index e5c17dd50..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/item/PistolItem.java +++ /dev/null @@ -1,45 +0,0 @@ -package mod.azure.azurelib.testing.item; - -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResultHolder; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import org.jetbrains.annotations.NotNull; - -public class PistolItem extends Item { - - private final PistolAnimationDispatcher dispatcher; - - public PistolItem() { - super(new Properties().stacksTo(1)); - this.dispatcher = new PistolAnimationDispatcher(); - } - - @Override - public void onUseTick( - @NotNull Level level, - @NotNull LivingEntity livingEntity, - @NotNull ItemStack stack, - int remainingUseDuration - ) { - super.onUseTick(level, livingEntity, stack, remainingUseDuration); - if (livingEntity instanceof Player player && !level.isClientSide()) { - dispatcher.serverFire(player, stack); - } - } - - @Override - public @NotNull InteractionResultHolder use( - @NotNull Level world, - Player user, - @NotNull InteractionHand hand - ) { - final var itemStack = user.getItemInHand(hand); - user.startUsingItem(hand); - return InteractionResultHolder.consume(itemStack); - } -} diff --git a/common/src/main/java/mod/azure/azurelib/testing/item/PistolRenderer.java b/common/src/main/java/mod/azure/azurelib/testing/item/PistolRenderer.java deleted file mode 100644 index 26b907d7f..000000000 --- a/common/src/main/java/mod/azure/azurelib/testing/item/PistolRenderer.java +++ /dev/null @@ -1,21 +0,0 @@ -package mod.azure.azurelib.testing.item; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.render.item.AzItemRenderer; -import mod.azure.azurelib.rewrite.render.item.AzItemRendererConfig; -import net.minecraft.resources.ResourceLocation; - -public class PistolRenderer extends AzItemRenderer { - - private static final ResourceLocation MODEL = AzureLib.modResource("geo/item/pistol.geo.json"); - - private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/item/pistol.png"); - - public PistolRenderer() { - super( - AzItemRendererConfig.builder(itemStack -> MODEL, itemStack -> TEXTURE) - .setAnimatorProvider(PistolAnimator::new) - .build() - ); - } -} diff --git a/common/src/main/resources/assets/azurelib/animations/block/stargate.animation.json b/common/src/main/resources/assets/azurelib/animations/block/stargate.animation.json deleted file mode 100644 index fac7d0e36..000000000 --- a/common/src/main/resources/assets/azurelib/animations/block/stargate.animation.json +++ /dev/null @@ -1,249 +0,0 @@ -{ - "format_version": "1.8.0", - "animations": { - "spinning": { - "loop": true, - "animation_length": 14.4, - "bones": { - "bone2": { - "position": { - "1.0": { - "vector": [0, 0, 0] - }, - "1.08": { - "vector": [0, "math.sin(query.anim_time * 360) * -3", 0] - }, - "1.12": { - "vector": [0, 0, 0] - }, - "1.56": { - "vector": [0, 0, 0] - }, - "2.64": { - "vector": [0, 0, 0] - }, - "2.72": { - "vector": [0, "math.cos(query.anim_time * 360) * -3", 0] - }, - "2.76": { - "vector": [0, 0, 0] - }, - "3.2": { - "vector": [0, 0, 0] - }, - "4.32": { - "vector": [0, 0, 0] - }, - "4.4": { - "vector": [0, "math.sin(query.anim_time * 360) * -2", 0] - }, - "4.44": { - "vector": [0, 0, 0] - }, - "4.88": { - "vector": [0, 0, 0] - }, - "6.0": { - "vector": [0, 0, 0] - }, - "6.08": { - "vector": [0, "math.sin(query.anim_time * 360) * -3", 0] - }, - "6.12": { - "vector": [0, 0, 0] - }, - "6.56": { - "vector": [0, 0, 0] - }, - "7.68": { - "vector": [0, 0, 0] - }, - "7.76": { - "vector": [0, "math.cos(query.anim_time * 360) * -3", 0] - }, - "7.8": { - "vector": [0, 0, 0] - }, - "8.24": { - "vector": [0, 0, 0] - }, - "9.36": { - "vector": [0, 0, 0] - }, - "9.44": { - "vector": [0, "math.sin(query.anim_time * 360) * -3", 0] - }, - "9.48": { - "vector": [0, 0, 0] - }, - "9.92": { - "vector": [0, 0, 0] - }, - "10.96": { - "vector": [0, 0, 0] - }, - "11.04": { - "vector": [0, "math.sin(query.anim_time * 360) * -3", 0] - }, - "11.08": { - "vector": [0, 0, 0] - }, - "11.52": { - "vector": [0, 0, 0] - } - } - }, - "inner_ring": { - "rotation": { - "0.0": { - "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] - }, - "1.04": { - "vector": [0, 0, 360] - }, - "1.48": { - "vector": [0, 0, 359] - }, - "1.52": { - "vector": [0, 0, "math.cos(query.anim_time * 360) * -360"] - }, - "2.56": { - "vector": [0, 0, 1] - }, - "3.16": { - "vector": [0, 0, 1] - }, - "3.2": { - "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] - }, - "4.32": { - "vector": [0, 0, 1] - }, - "5.24": { - "vector": [0, 0, 1] - }, - "5.28": { - "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] - }, - "5.88": { - "vector": [0, 0, 1] - }, - "5.92": { - "vector": [0, 0, 0] - }, - "6.92": { - "vector": [0, 0, 0] - }, - "6.96": { - "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] - }, - "7.56": { - "vector": [0, 0, 1] - }, - "7.6": { - "vector": [0, 0, 0] - }, - "8.12": { - "vector": [0, 0, 0] - }, - "8.16": { - "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] - }, - "9.2": { - "vector": [0, 0, 1] - }, - "9.28": { - "vector": [0, 0, 0] - }, - "9.84": { - "vector": [0, 0, 0] - }, - "10.88": { - "vector": [0, 0, 359] - }, - "10.96": { - "vector": [0, 0, 360] - } - } - }, - "portal": { - "scale": { - "0.0": { - "vector": [0, 0, 0] - }, - "11.48": { - "vector": [0, 0, 0] - }, - "11.52": { - "vector": [1, 1, 1] - }, - "12.4": { - "vector": [1, 1, 1] - } - } - }, - "portal2": { - "rotation": { - "11.52": { - "vector": [0, 0, "math.cos(query.anim_time * 360) * 360"] - }, - "13.44": { - "vector": [0, 0, 359] - } - }, - "position": { - "11.52": { - "vector": [0, 0, 0] - }, - "12.4": { - "vector": [0, 0, "-30.83+math.sin(query.anim_time * 200) * 10"] - }, - "13.44": { - "vector": [0, 0, 0] - } - }, - "scale": { - "0.0": { - "vector": [0, 0, 0] - }, - "11.52": { - "vector": [0, 0, 0] - }, - "12.4": { - "vector": [0.5, 0.5, 37.3] - }, - "13.44": { - "vector": [0, 0, 0] - } - } - } - } - }, - "idle": { - "loop": true, - "bones": { - "portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "portal2": { - "scale": { - "vector": [0, 0, 0] - } - } - } - }, - "open": { - "loop": true, - "bones": { - "bone9": { - "scale": { - "vector": [1, 1, 1] - } - } - } - } - }, - "azurelib_format_version": 2 -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/animations/entity/marauder.animation.json b/common/src/main/resources/assets/azurelib/animations/entity/marauder.animation.json deleted file mode 100644 index 94ee538c4..000000000 --- a/common/src/main/resources/assets/azurelib/animations/entity/marauder.animation.json +++ /dev/null @@ -1,6262 +0,0 @@ -{ - "format_version": "1.8.0", - "animations": { - "idle": { - "loop": true, - "animation_length": 2, - "bones": { - "torso": { - "rotation": { - "vector": [0, 22.5, 0] - } - }, - "right_arm": { - "rotation": { - "vector": [36.94319, 29.52361, 9.71415] - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0.5, 0] - }, - "2.0": { - "vector": [0, 0, 0] - } - } - }, - "left_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.2": { - "vector": [-2.83, 0, 0] - }, - "0.8": { - "vector": [-7.17, 0, 0] - }, - "1.0": { - "vector": [-7.5, 0, 0] - }, - "1.2": { - "vector": [-7.17, 0, 0] - }, - "1.8": { - "vector": [-2.83, 0, 0] - }, - "2.0": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, -1.5] - }, - "0.2": { - "vector": [0, -0.25, -1.5] - }, - "0.8": { - "vector": [0, -0.25, -1.5] - }, - "1.0": { - "vector": [0, -0.25, -1.5] - }, - "1.2": { - "vector": [0, -0.25, -1.5] - }, - "1.8": { - "vector": [0, -0.25, -1.5] - }, - "2.0": { - "vector": [0, -0.25, -1.5] - } - } - }, - "left_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "0.2": { - "vector": [5.67, 0, 0] - }, - "0.8": { - "vector": [14.33, 0, 0] - }, - "1.0": { - "vector": [15, 0, 0] - }, - "1.2": { - "vector": [14.33, 0, 0] - }, - "1.8": { - "vector": [5.67, 0, 0] - }, - "2.0": { - "vector": [5, 0, 0] - } - } - }, - "right_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.2": { - "vector": [-2.83, 0, 0] - }, - "0.8": { - "vector": [-7.17, 0, 0] - }, - "1.0": { - "vector": [-7.5, 0, 0] - }, - "1.2": { - "vector": [-7.17, 0, 0] - }, - "1.8": { - "vector": [-2.83, 0, 0] - }, - "2.0": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, 0.5] - }, - "0.2": { - "vector": [0, -0.25, 0.5] - }, - "0.8": { - "vector": [0, -0.25, 0.5] - }, - "1.0": { - "vector": [0, -0.25, 0.5] - }, - "1.2": { - "vector": [0, -0.25, 0.5] - }, - "1.8": { - "vector": [0, -0.25, 0.5] - }, - "2.0": { - "vector": [0, -0.25, 0.5] - } - } - }, - "right_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "0.2": { - "vector": [5.67, 0, 0] - }, - "0.8": { - "vector": [14.33, 0, 0] - }, - "1.0": { - "vector": [15, 0, 0] - }, - "1.2": { - "vector": [14.33, 0, 0] - }, - "1.8": { - "vector": [5.67, 0, 0] - }, - "2.0": { - "vector": [5, 0, 0] - } - } - }, - "h_head_furious": { - "rotation": { - "vector": [0, -22.5, 0] - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0.25, 0] - }, - "2.0": { - "vector": [0, 0, 0] - } - } - }, - "h_slash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "inactive_axe": { - "scale": { - "vector": [0, 0, 0] - } - }, - "leg_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "body2": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, -0.5, 0] - }, - "2.0": { - "vector": [0, 0, 0] - } - } - }, - "dreadnought_portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_attack": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_normal": { - "scale": { - "vector": [1, 1, 1] - } - } - } - }, - "walk": { - "loop": true, - "animation_length": 2, - "bones": { - "torso": { - "rotation": { - "vector": [0, 22.5, 0] - }, - "position": { - "0.0": { - "vector": [-0.25, 0, 0] - }, - "0.3": { - "vector": [-0.33, 0, 0] - }, - "1.0": { - "vector": [0.25, 0, 0] - }, - "1.3": { - "vector": [0.33, 0, 0] - }, - "2.0": { - "vector": [-0.25, 0, 0] - } - } - }, - "upper_torso": { - "rotation": { - "0.0": { - "vector": [0, 0, 1] - }, - "0.3": { - "vector": [0, 0, 1.5] - }, - "1.0": { - "vector": [0, 0, -1] - }, - "1.3": { - "vector": [0, 0, -1.5] - }, - "2.0": { - "vector": [0, 0, 1] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [0, -0.17, 0] - }, - "0.2": { - "vector": [0, -0.83, 0] - }, - "0.3": { - "vector": [0, -1, 0] - }, - "0.45": { - "vector": [0, -0.93, 0] - }, - "0.85": { - "vector": [0, -0.07, 0] - }, - "1.0": { - "vector": [0, 0, 0] - }, - "1.1": { - "vector": [0, -0.17, 0] - }, - "1.2": { - "vector": [0, -0.83, 0] - }, - "1.3": { - "vector": [0, -1, 0] - }, - "1.45": { - "vector": [0, -0.93, 0] - }, - "1.85": { - "vector": [0, -0.07, 0] - }, - "2.0": { - "vector": [0, 0, 0] - } - } - }, - "right_arm": { - "rotation": { - "0.0": { - "vector": [36.94319, 29.52361, 9.71415] - }, - "0.3": { - "vector": [37.94, 29.52, 9.71] - }, - "1.0": { - "vector": [37.1047, 28.17926, 12.12078] - }, - "1.3": { - "vector": [36.1047, 28.17926, 12.12078] - }, - "2.0": { - "vector": [36.94319, 29.52361, 9.71415] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [0, 0.17, 0] - }, - "0.2": { - "vector": [0, 0.83, 0] - }, - "0.3": { - "vector": [0, 1, 0] - }, - "0.45": { - "vector": [0, 0.93, 0] - }, - "0.85": { - "vector": [0, 0.07, 0] - }, - "1.0": { - "vector": [0, 0, 0] - }, - "1.1": { - "vector": [0, 0.17, 0] - }, - "1.2": { - "vector": [0, 0.83, 0] - }, - "1.3": { - "vector": [0, 1, 0] - }, - "1.45": { - "vector": [0, 0.93, 0] - }, - "1.85": { - "vector": [0, 0.07, 0] - }, - "2.0": { - "vector": [0, 0, 0] - } - } - }, - "left_leg": { - "rotation": { - "0.0": { - "vector": [12.5, 0, 0] - }, - "0.55": { - "vector": [-10, 0, 0] - }, - "1.0": { - "vector": [-9.96271, 0.86717, 4.92442] - }, - "2.0": { - "vector": [12.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, -0.5, -1] - }, - "0.15": { - "vector": [0, 1, -2] - }, - "0.55": { - "vector": [0, 1, -2] - }, - "1.0": { - "vector": [0, -1, -2.5] - }, - "2.0": { - "vector": [0, -0.5, -1] - } - } - }, - "left_knee": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [32.5, 0, 0] - }, - "1.0": { - "vector": [5, 0, 0] - }, - "2.0": { - "vector": [0, 0, 0] - } - } - }, - "right_leg": { - "rotation": { - "0.0": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "1.0": { - "vector": [12.5, 0, 0] - }, - "1.55": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "2.0": { - "vector": [-9.96271, -0.86717, -4.92442] - } - }, - "position": { - "0.0": { - "vector": [0, -1, -0.5] - }, - "1.0": { - "vector": [0, -0.5, 1] - }, - "1.15": { - "vector": [0, 1, 0] - }, - "1.55": { - "vector": [0, 1, 0] - }, - "2.0": { - "vector": [0, -1, -0.5] - } - } - }, - "right_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "1.0": { - "vector": [0, 0, 0] - }, - "1.3": { - "vector": [32.5, 0, 0] - }, - "2.0": { - "vector": [5, 0, 0] - } - } - }, - "h_head_furious": { - "rotation": { - "0.0": { - "vector": [0, -22.5, -1] - }, - "0.3": { - "vector": [0, -22.5, -1.5] - }, - "1.0": { - "vector": [0, -22.5, 1] - }, - "1.3": { - "vector": [0, -22.5, 1.5] - }, - "2.0": { - "vector": [0, -22.5, -1] - } - } - }, - "inactive_axe": { - "scale": { - "vector": [0, 0, 0] - } - }, - "leg_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "h_slash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "dreadnought_portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_attack": { - "scale": { - "vector": [0, 0, 0] - } - } - }, - "sound_effects": { - "0.95": { - "effect": "walk" - }, - "1.95": { - "effect": "walk" - } - } - }, - "run": { - "loop": true, - "animation_length": 0.5, - "bones": { - "torso": { - "rotation": { - "vector": [25, 0, 0] - } - }, - "left_arm": { - "rotation": { - "0.0": { - "vector": [-47.41709, -12.48859, 0.54146] - }, - "0.1": { - "vector": [-16.79911, -16.90142, 4.98561] - }, - "0.25": { - "vector": [39.3969, -19.7052, 3.8848] - }, - "0.5": { - "vector": [-47.41709, -12.48859, 0.54146] - } - } - }, - "left_elbow": { - "rotation": { - "0.0": { - "vector": [-27.5, 0, 0] - }, - "0.5": { - "vector": [-27.5, 0, 0] - } - } - }, - "right_arm": { - "rotation": { - "0.0": { - "vector": [39.3969, 19.7052, -3.8848] - }, - "0.25": { - "vector": [-47.41709, 12.4886, -0.5415] - }, - "0.35": { - "vector": [-16.79911, 16.9014, -4.9856] - }, - "0.5": { - "vector": [39.3969, 19.7052, -3.8848] - } - } - }, - "right_elbow": { - "rotation": { - "0.0": { - "vector": [-27.5, 0, 0] - }, - "0.5": { - "vector": [-27.5, 0, 0] - } - } - }, - "axe": { - "rotation": { - "vector": [-45, -45, 0] - } - }, - "left_leg": { - "rotation": { - "0.0": { - "vector": [48.84318, 11.0235, -2.4076] - }, - "0.25": { - "vector": [-47.31623, 14.328, 1.6962] - }, - "0.35": { - "vector": [-16.25675, 7.3218, 2.8407] - }, - "0.5": { - "vector": [48.84318, 11.0235, -2.4076] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, -1, -2] - }, - "0.5": { - "vector": [0, 0, 0] - } - } - }, - "left_knee": { - "rotation": { - "0.0": { - "vector": [41.96457, 0, 0] - }, - "0.1": { - "vector": [22.01779, 0, 0] - }, - "0.25": { - "vector": [10.44841, 0, 0] - }, - "0.5": { - "vector": [41.96457, 0, 0] - } - } - }, - "right_leg": { - "rotation": { - "0.0": { - "vector": [-47.31623, -14.32798, -1.69619] - }, - "0.1": { - "vector": [-16.25675, -7.32179, -2.84073] - }, - "0.25": { - "vector": [48.84318, -11.02346, 2.40756] - }, - "0.5": { - "vector": [-47.31623, -14.32798, -1.69619] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [0, -1, -2] - }, - "0.25": { - "vector": [0, 0, 0] - }, - "0.5": { - "vector": [0, 0, 0] - } - } - }, - "right_knee": { - "rotation": { - "0.0": { - "vector": [10.44841, 0, 0] - }, - "0.25": { - "vector": [41.96457, 0, 0] - }, - "0.35": { - "vector": [22.01779, 0, 0] - }, - "0.5": { - "vector": [10.44841, 0, 0] - } - } - }, - "h_head_furious": { - "rotation": { - "vector": [-15, 0, 0] - } - }, - "inactive_axe": { - "scale": { - "vector": [0, 0, 0] - } - }, - "leg_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "h_slash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "body2": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [0, -2, 0] - }, - "0.2": { - "vector": [0, 1.5, 0] - }, - "0.25": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, -2, 0] - }, - "0.45": { - "vector": [0, 1.5, 0] - }, - "0.5": { - "vector": [0, 0, 0] - } - } - }, - "dreadnought_portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_attack": { - "scale": { - "vector": [0, 0, 0] - } - } - }, - "sound_effects": { - "0.05": { - "effect": "walk" - }, - "0.15": { - "effect": "walk" - }, - "0.3": { - "effect": "walk" - }, - "0.4": { - "effect": "walk" - } - } - }, - "energy_slash": { - "animation_length": 1, - "bones": { - "torso": { - "rotation": { - "0.0": { - "vector": [0, 22.5, 0] - }, - "0.25": { - "vector": [-22.45376, 4.88119, 1.08482], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [20.15711, -30.42471, 3.15289], - "easing": "easeInOutQuad" - }, - "1.0": { - "vector": [0, 22.5, 0], - "easing": "easeInOutQuad" - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.2": { - "vector": [0, 0.05, 0] - }, - "0.25": { - "vector": [0, 0.8, 0] - }, - "0.4": { - "vector": [0, 0.95, 0] - }, - "0.5": { - "vector": [0, 0.95, 0] - }, - "0.65": { - "vector": [0, 0.8, 0] - }, - "1.0": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - } - } - }, - "left_arm": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-42.73421, -15.69986, -38.82495], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [-25.00772, 13.4395, -26.99582], - "easing": "easeInOutQuad" - }, - "1.0": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - } - } - }, - "left_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-45, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [-22.5, 0, 0], - "easing": "easeInOutQuad" - }, - "1.0": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - } - } - }, - "right_arm": { - "rotation": { - "0.0": { - "vector": [36.94319, 29.52361, 9.71415] - }, - "0.25": { - "vector": [-170.91589, -12.68945, -29.87236], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [-50.77424, 27.00637, -42.94328], - "easing": "easeInOutQuad" - }, - "0.5": { - "vector": [-21.42177, 17.7925, -31.36258], - "easing": "easeInOutQuad" - }, - "1.0": { - "vector": [36.94319, 29.52361, 9.71415], - "easing": "easeInOutQuad" - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - } - } - }, - "axe": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-45, -45, 15], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [22.5, -45, 15], - "easing": "easeInOutQuad" - }, - "1.0": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - } - } - }, - "left_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.25": { - "vector": [-56.86218, 2.34352, -12.28179] - }, - "0.4": { - "vector": [-32.5, 0, 0] - }, - "0.5": { - "vector": [-32.5, 0, 0] - }, - "1.0": { - "vector": [-2.5, 0, 0], - "easing": "easeInOutQuad" - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, -1.5] - }, - "1.0": { - "vector": [0, -0.25, -1.5], - "easing": "easeInOutQuad" - } - } - }, - "left_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "0.25": { - "vector": [45, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [45, 0, 0], - "easing": "easeInOutQuad" - }, - "0.5": { - "vector": [45, 0, 0], - "easing": "easeInOutQuad" - }, - "1.0": { - "vector": [5, 0, 0], - "easing": "easeInOutQuad" - } - } - }, - "right_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.25": { - "vector": [10.4943, -0.822, -2.71827] - }, - "0.4": { - "vector": [37.5, 0, 0] - }, - "0.5": { - "vector": [37.5, 0, 0] - }, - "1.0": { - "vector": [-2.5, 0, 0], - "easing": "easeInOutQuad" - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, 0.5] - }, - "1.0": { - "vector": [0, -0.25, 0.5], - "easing": "easeInOutQuad" - } - } - }, - "right_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "1.0": { - "vector": [5, 0, 0], - "easing": "easeInOutQuad" - } - } - }, - "h_slash": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 4, -8] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - }, - "scale": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.35": { - "vector": [1, 1, 1], - "easing": "easeInOutQuad" - } - } - }, - "h_slash2": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - } - }, - "h_slash3": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - } - }, - "h_slash4": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - } - }, - "h_slash5": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - } - }, - "h_slash6": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - } - }, - "h_slash7": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - } - }, - "h_slash8": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - } - }, - "h_slash9": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - } - }, - "h_slash10": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - }, - "0.4": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, -47], - "easing": "easeInOutQuad" - } - } - }, - "h_head_furious": { - "rotation": { - "0.0": { - "vector": [0, -22.5, 0] - }, - "0.25": { - "vector": [33.77048, -5.99032, 2.66499] - }, - "0.4": { - "vector": [-5.12161, 25.82329, -2.83601] - }, - "0.5": { - "vector": [-5.12161, 25.82329, -2.83601] - }, - "0.75": { - "vector": [-0.75543, 4.97912, -2.45465] - }, - "1.0": { - "vector": [0, -22.5, 0], - "easing": "easeInOutQuad" - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - } - } - }, - "inactive_axe": { - "scale": { - "vector": [0, 0, 0] - } - }, - "leg_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "body2": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.15": { - "vector": [0, -1, 2] - }, - "0.3": { - "vector": [0, -1, 2] - }, - "0.4": { - "vector": [0, -4, -8] - }, - "1.0": { - "vector": [0, 0, 0], - "easing": "easeInOutQuad" - } - } - }, - "dreadnought_portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_normal": { - "scale": { - "vector": [0, 0, 0] - } - } - }, - "sound_effects": { - "0.35": { - "effect": "slash" - } - } - }, - "hook": { - "animation_length": 1, - "bones": { - "torso": { - "rotation": { - "0.0": { - "vector": [0, 22.5, 0] - }, - "0.35": { - "vector": [0, -45, 0] - }, - "0.5": { - "vector": [30, 45, 0] - }, - "0.6": { - "vector": [63.10819, 71.24264, 36.51548] - }, - "1.0": { - "vector": [0, 22.5, 0] - } - } - }, - "left_arm": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.4": { - "vector": [62.65725, 6.55676, -77.51703] - }, - "0.5": { - "vector": [-67.34275, 6.55676, -77.51703] - }, - "0.6": { - "vector": [-104.84275, 6.55676, -77.51703] - }, - "1.0": { - "vector": [0, 0, 0] - } - } - }, - "left_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-60, 0, 0] - }, - "0.4": { - "vector": [-91.5, 0, 0] - }, - "0.5": { - "vector": [-37.5, 0, 0] - }, - "0.6": { - "vector": [-30.5, 0, 0] - }, - "1.0": { - "vector": [0, 0, 0] - } - } - }, - "right_arm": { - "rotation": { - "0.0": { - "vector": [36.94319, 29.52361, 9.71415] - }, - "0.35": { - "vector": [42.52426, 19.88275, 12.4118] - }, - "0.4": { - "vector": [42.52426, 19.88275, 12.4118] - }, - "0.45": { - "vector": [10.82416, 8.65125, 9.70546] - }, - "0.5": { - "vector": [-5.0275, -2.11363, 18.00718] - }, - "0.6": { - "vector": [-14.10919, -0.72303, 17.14132] - }, - "1.0": { - "vector": [36.94319, 29.52361, 9.71415] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [1, 0, 1] - }, - "0.5": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, 0] - } - } - }, - "right_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [22.5, 0, 0] - }, - "0.35": { - "vector": [22.5, 0, 0] - }, - "0.4": { - "vector": [0, 0, 0] - }, - "0.5": { - "vector": [0, 0, 0] - }, - "0.6": { - "vector": [7.5, 0, 0] - }, - "1.0": { - "vector": [0, 0, 0] - } - } - }, - "left_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.4": { - "vector": [-7.5, 0, 0] - }, - "0.5": { - "vector": [-2.5, 0, 0] - }, - "1.0": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, -1.5] - }, - "0.5": { - "vector": [0, -0.25, -1.5] - }, - "1.0": { - "vector": [0, -0.25, -1.5] - } - } - }, - "left_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "0.4": { - "vector": [12.5, 0, 0] - }, - "0.5": { - "vector": [5, 0, 0] - }, - "1.0": { - "vector": [5, 0, 0] - } - } - }, - "right_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.4": { - "vector": [-7.5, 0, 0] - }, - "0.5": { - "vector": [-2.5, 0, 0] - }, - "1.0": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, 0.5] - }, - "0.5": { - "vector": [0, -0.25, -1.5] - }, - "1.0": { - "vector": [0, -0.25, 0.5] - } - } - }, - "right_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "0.4": { - "vector": [12.5, 0, 0] - }, - "0.5": { - "vector": [5, 0, 0] - }, - "1.0": { - "vector": [5, 0, 0] - } - } - }, - "h_head_furious": { - "rotation": { - "0.0": { - "vector": [0, -22.5, 0] - }, - "0.25": { - "vector": [18.81517, 32.16683, 2.79164] - }, - "0.3": { - "vector": [17.23042, 35.45167, 3.72115] - }, - "0.35": { - "vector": [15.61275, 38.78143, 4.50441] - }, - "0.4": { - "vector": [1.82829, 15.09442, -2.58693] - }, - "0.5": { - "vector": [-3.83528, -41.13981, 7.84775] - }, - "0.6": { - "vector": [-20.07884, -63.15509, 26.26946] - }, - "0.8": { - "vector": [-0.62387, -43.76225, -0.56045] - }, - "1.0": { - "vector": [0, -22.5, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, 0] - } - } - }, - "inactive_axe": { - "scale": { - "vector": [0, 0, 0] - } - }, - "leg_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "h_slash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "body2": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 0, 0] - } - } - }, - "dreadnought_portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_normal": { - "scale": { - "vector": [0, 0, 0] - } - } - }, - "sound_effects": { - "0.45": { - "effect": "axe_hit" - } - } - }, - "shoot": { - "animation_length": 1.25, - "bones": { - "torso": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [0, 3.75, 0] - }, - "0.2": { - "vector": [0, 18.75, 0] - }, - "0.3": { - "vector": [0, 22.5, 0] - }, - "0.55": { - "vector": [0, 22.5, 0] - }, - "0.75": { - "vector": [0, 22.5, 0] - }, - "0.85": { - "vector": [0, 20.89, 0] - }, - "1.15": { - "vector": [0, 1.61, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "upper_torso": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [0, 3.75, 0] - }, - "0.2": { - "vector": [0, 18.75, 0] - }, - "0.3": { - "vector": [0, 22.5, 0] - }, - "0.55": { - "vector": [0, 22.5, 0] - }, - "0.65": { - "vector": [-3.53092, 22.36846, -2.70099] - }, - "0.75": { - "vector": [-3.53, 22.37, -2.7] - }, - "0.85": { - "vector": [-3.28, 20.77, -2.51] - }, - "1.15": { - "vector": [-0.25, 1.6, -0.19] - }, - "1.25": { - "vector": [0, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.65": { - "vector": [0, 0, 1] - }, - "0.75": { - "vector": [0, 0, 0] - }, - "0.85": { - "vector": [0, 0, 0] - }, - "1.15": { - "vector": [0, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "left_arm": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [-10.25, -7.3, 2.45] - }, - "0.2": { - "vector": [-51.27, -36.49, 12.26] - }, - "0.3": { - "vector": [-61.52003, -43.79162, 14.71526] - }, - "0.55": { - "vector": [-61.52003, -43.79162, 14.71526] - }, - "0.65": { - "vector": [-106.52003, -43.79162, 14.71526] - }, - "0.75": { - "vector": [-106.52, -43.79, 14.72] - }, - "0.85": { - "vector": [-98.91, -40.66, 13.67] - }, - "1.15": { - "vector": [-7.61, -3.13, 1.05] - }, - "1.25": { - "vector": [0, 0, 0] - } - }, - "position": { - "0.55": { - "vector": [0, 0, 0] - }, - "0.65": { - "vector": [0, 0, 1] - }, - "0.75": { - "vector": [0, 0, 0] - }, - "0.85": { - "vector": [0, 0, 0] - } - } - }, - "left_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.65": { - "vector": [-22.5, 0, 0] - }, - "0.75": { - "vector": [-22.5, 0, 0] - }, - "0.85": { - "vector": [-20.89, 0, 0] - }, - "1.15": { - "vector": [-1.61, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "right_arm": { - "rotation": { - "0.0": { - "vector": [36.94319, 29.52361, 9.71415] - }, - "0.1": { - "vector": [32.45, 24.6, 8.1] - }, - "0.2": { - "vector": [14.49, 4.92, 1.62] - }, - "0.3": { - "vector": [10, 0, 0] - }, - "0.75": { - "vector": [10, 0, 0] - }, - "0.85": { - "vector": [11.68, 1.85, 0.61] - }, - "1.15": { - "vector": [35.26, 27.68, 9.11] - }, - "1.25": { - "vector": [36.94319, 29.52361, 9.71415] - } - } - }, - "h_head_furious": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [0.83, -7.5, 0] - }, - "0.2": { - "vector": [4.17, -37.5, 0] - }, - "0.3": { - "vector": [5, -45, 0] - }, - "0.4": { - "vector": [5, -45, 0] - }, - "0.75": { - "vector": [5, -45, 0] - }, - "0.85": { - "vector": [4.64, -41.79, 0] - }, - "1.15": { - "vector": [0.2, -1.77, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "inactive_axe": { - "scale": { - "vector": [0, 0, 0] - } - }, - "leg_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "h_slash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "body2": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.65": { - "vector": [-2.5, 0, 0] - }, - "0.75": { - "vector": [0, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.65": { - "vector": [0, 0, 0.7] - }, - "0.75": { - "vector": [0, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "dreadnought_portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_normal": { - "scale": { - "vector": [0, 0, 0] - } - } - }, - "sound_effects": { - "0.45": { - "effect": "attack" - } - } - }, - "axe_attack": { - "animation_length": 1.25, - "override_previous_animation": true, - "bones": { - "torso": { - "rotation": { - "0.0": { - "vector": [0, 22.5, 0] - }, - "0.3": { - "vector": [0, 0, 0] - }, - "0.5": { - "vector": [0, 0, 0] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.6": { - "vector": [0, 19.42, 0] - }, - "0.8": { - "vector": [0, -15.24, 0] - }, - "1.25": { - "vector": [0, 22.5, 0] - } - } - }, - "upper_torso": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [0, 45, 0] - }, - "0.5": { - "vector": [0, 45, 0] - }, - "0.55": { - "vector": [0, -7.5, 0] - }, - "0.6": { - "vector": [0, 16.15, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "left_arm": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [-15.50853, 9.3733, -19.69937] - }, - "0.5": { - "vector": [-15.50853, 9.3733, -19.69937] - }, - "0.6": { - "vector": [20.14197, 18.45386, -44.18719] - }, - "0.9": { - "vector": [21.31475, -5.36143, -32.73013] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "left_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [-67.5, 0, 0] - }, - "0.5": { - "vector": [-67.5, 0, 0] - }, - "0.6": { - "vector": [-17.31, 0, 0] - }, - "0.9": { - "vector": [-17.31, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "right_arm": { - "rotation": { - "0.0": { - "vector": [36.94319, 29.52361, 9.71415] - }, - "0.3": { - "vector": [-109.70372, -34.12145, 43.97409] - }, - "0.5": { - "vector": [-121.22829, -24.29511, 67.25777] - }, - "0.55": { - "vector": [-102.45958, -14.42399, 77.57976] - }, - "0.6": { - "vector": [-29.07197, -13.47349, 60.63172] - }, - "0.7": { - "vector": [37.73643, -0.99934, 60.90401] - }, - "0.8": { - "vector": [52.73643, -0.99934, 60.90401] - }, - "1.25": { - "vector": [36.94319, 29.52361, 9.71415] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [2, -1, -1] - }, - "0.5": { - "vector": [2, -1, -1] - }, - "0.6": { - "vector": [2, 0, 0] - }, - "0.7": { - "vector": [2, 0, 0] - }, - "0.8": { - "vector": [2, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "right_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [0, 0, 0] - }, - "0.5": { - "vector": [0, 0, 0] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "axe": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [27.44634, -46.77638, 1.64566] - }, - "0.2": { - "vector": [12.8954, -72.37008, -0.60389] - }, - "0.3": { - "vector": [-1.09992, -78.07078, 2.27213] - }, - "0.5": { - "vector": [-19.66347, -69.27795, 32.98256] - }, - "0.55": { - "vector": [-26.9171, -41.80641, 26.33413] - }, - "0.6": { - "vector": [-26.9171, -41.80641, 26.33413] - }, - "0.7": { - "vector": [-4.4171, -41.80641, 26.33413] - }, - "0.75": { - "vector": [18.0829, -41.80641, 26.33413] - }, - "0.8": { - "vector": [40.32194, -42.2031, 21.34306] - }, - "1.25": { - "vector": [0, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.15": { - "vector": [0.8, -0.8, -2] - }, - "0.3": { - "vector": [0, 0, -1] - }, - "0.5": { - "vector": [0, 0, -1] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.6": { - "vector": [0, 0, 0] - }, - "0.7": { - "vector": [0, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "left_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.3": { - "vector": [-2.5, 0, 0] - }, - "0.5": { - "vector": [-2.5, 0, 0] - }, - "0.55": { - "vector": [-2.5, 0, 0] - }, - "0.6": { - "vector": [-18.7289, 36.8678, 1.46479] - }, - "0.65": { - "vector": [-39.75355, 33.51618, 1.33163] - }, - "1.25": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, -1.5] - }, - "0.3": { - "vector": [0, -0.25, -1.5] - }, - "0.5": { - "vector": [0, -0.25, -1.5] - }, - "0.55": { - "vector": [0, -0.25, -1.5] - }, - "1.25": { - "vector": [0, -0.25, -1.5] - } - } - }, - "left_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "0.3": { - "vector": [5, 0, 0] - }, - "0.45": { - "vector": [5, 0, 0] - }, - "0.55": { - "vector": [5, 0, 0] - }, - "0.6": { - "vector": [27.5, 0, 0] - }, - "0.65": { - "vector": [47.95, 0, 0] - }, - "1.25": { - "vector": [5, 0, 0] - } - } - }, - "right_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.3": { - "vector": [-2.5, 0, 0] - }, - "0.45": { - "vector": [-21.38885, 10.86362, 32.59688] - }, - "0.55": { - "vector": [-20.35186, 1.51634, 36.30248] - }, - "0.6": { - "vector": [27.22998, 57.15097, 37.99251] - }, - "1.25": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, 0.5] - }, - "0.3": { - "vector": [0, -0.25, 0.5] - }, - "0.45": { - "vector": [0, -0.25, 0.5] - }, - "0.55": { - "vector": [0, -0.25, 0.5] - }, - "1.25": { - "vector": [0, -0.25, 0.5] - } - } - }, - "right_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "0.3": { - "vector": [5, 0, 0] - }, - "0.45": { - "vector": [72.5, 0, 0] - }, - "0.55": { - "vector": [72.5, 0, 0] - }, - "0.6": { - "vector": [27.5, 0, 0] - }, - "1.25": { - "vector": [5, 0, 0] - } - } - }, - "h_head_furious": { - "rotation": { - "0.0": { - "vector": [0, -22.5, 0] - }, - "0.3": { - "vector": [0, 72.5, 0] - }, - "0.5": { - "vector": [0, 72.5, 0] - }, - "0.55": { - "vector": [0, 45, 0] - }, - "0.6": { - "vector": [0, 22.5, 0] - }, - "0.65": { - "vector": [0, 22.92, 0] - }, - "1.25": { - "vector": [0, -22.5, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [0, 0, 0] - }, - "0.5": { - "vector": [0, 0, 0] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.6": { - "vector": [0, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "inactive_axe": { - "scale": { - "vector": [0, 0, 0] - } - }, - "leg_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "h_slash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "body2": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.15": { - "vector": [0, 75, 0] - }, - "0.3": { - "vector": [0, 100, 0] - }, - "0.5": { - "vector": [0, 220.04517, 0] - }, - "0.6": { - "vector": [0, 310.04517, 0] - }, - "0.65": { - "vector": [0, 310.04517, 0] - }, - "0.85": { - "vector": [0, 360, 0] - }, - "1.25": { - "vector": [0, 360, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [1, -1, 4] - }, - "0.4": { - "vector": [4.5, -1, 1] - }, - "0.45": { - "vector": [4.25, -1, -2.5] - }, - "0.5": { - "vector": [3, -1, -4] - }, - "0.55": { - "vector": [0, 0, -4] - }, - "0.65": { - "vector": [0, -4, -8] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "dreadnought_portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_normal": { - "scale": { - "vector": [0, 0, 0] - } - } - }, - "sound_effects": { - "0.25": { - "effect": "axe_hit" - } - } - }, - "axe_cut": { - "animation_length": 1.35, - "bones": { - "torso": { - "rotation": { - "0.0": { - "vector": [0, 22.5, 0] - }, - "0.3": { - "vector": [-12.5, 0, 0] - }, - "0.5": { - "vector": [0, 0, 0] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.6": { - "vector": [56.82632, 6.94314, -0.0723] - }, - "0.75": { - "vector": [56.82632, 6.94314, -0.0723] - }, - "1.25": { - "vector": [0, 22.5, 0] - } - } - }, - "upper_torso": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [0, 55, 0] - }, - "0.5": { - "vector": [0, 45, 0] - }, - "0.55": { - "vector": [0, 42, 0] - }, - "0.6": { - "vector": [0, -21, 0] - }, - "0.75": { - "vector": [0, -21, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - }, - "position": { - "vector": [0, 0, 0] - } - }, - "left_arm": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [0, 0, 0] - }, - "0.2": { - "vector": [-36.54219, -33.28982, -69.7671] - }, - "0.4": { - "vector": [-15.2553, -34.46461, -98.06686] - }, - "0.5": { - "vector": [-33.95742, -37.02514, -80.39611] - }, - "0.55": { - "vector": [-40.69539, -21.98867, -73.87325] - }, - "0.6": { - "vector": [-97.19705, 30.96904, -59.43905] - }, - "0.75": { - "vector": [-97.19705, 30.96904, -59.43905] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "left_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.3": { - "vector": [-45, 0, 0] - }, - "0.5": { - "vector": [-45, 0, 0] - }, - "0.6": { - "vector": [-34.81, 0, 0] - }, - "0.75": { - "vector": [-34.81, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "right_arm": { - "rotation": { - "0.0": { - "vector": [36.94319, 29.52361, 9.71415] - }, - "0.2": { - "vector": [-145.20556, -13.66588, 31.66413] - }, - "0.35": { - "vector": [-145.20556, -13.66588, 31.66413] - }, - "0.5": { - "vector": [-143.03203, -20.66768, 33.50612] - }, - "0.55": { - "vector": [-134.1591, -38.7886, 10.07665] - }, - "0.6": { - "vector": [-84.25071, -6.85667, -9.50735] - }, - "0.75": { - "vector": [-84.25071, -6.85667, -9.50735] - }, - "1.25": { - "vector": [36.94319, 29.52361, 9.71415] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.2": { - "vector": [1, 3, -1] - }, - "0.5": { - "vector": [2, 3, -2] - }, - "0.6": { - "vector": [2, 0, 0] - }, - "0.7": { - "vector": [2, 0, 0] - }, - "0.8": { - "vector": [2, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "right_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.2": { - "vector": [0, 0, 0] - }, - "0.5": { - "vector": [-12.5, 0, 0] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.75": { - "vector": [0, 0, 0] - }, - "1.2": { - "vector": [0, 0, 0] - } - } - }, - "axe": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.05": { - "vector": [-17.55366, -46.77638, 1.64566] - }, - "0.15": { - "vector": [-31.37378, -33.42989, -4.98105] - }, - "0.2": { - "vector": [-44.19581, -3.0647, 48.97981] - }, - "0.35": { - "vector": [-44.12388, 1.93476, 49.08245] - }, - "0.5": { - "vector": [-46.88452, -10.32122, 51.03982] - }, - "0.55": { - "vector": [-57.65366, -23.33192, 27.23873] - }, - "0.6": { - "vector": [16.98134, -43.05147, 42.21258] - }, - "1.25": { - "vector": [0, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.1": { - "vector": [0.8, -0.8, -2] - }, - "0.2": { - "vector": [0, 0, -1] - }, - "0.5": { - "vector": [0, 0, -1] - }, - "0.55": { - "vector": [0, 0, 0] - }, - "0.6": { - "vector": [0, 0, 0] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "left_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.3": { - "vector": [-2.5, 0, 0] - }, - "0.55": { - "vector": [12.5, 0, 0] - }, - "0.6": { - "vector": [12.5, 0, 0] - }, - "0.65": { - "vector": [12.5, 0, 0] - }, - "0.7": { - "vector": [12.5, 0, 0] - }, - "0.75": { - "vector": [12.5, 0, 0] - }, - "1.25": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, -1.5] - }, - "0.3": { - "vector": [0, -0.25, -1.5] - }, - "0.6": { - "vector": [0, -0.25, -1.5] - }, - "0.65": { - "vector": [0, -0.25, -1.5] - }, - "1.25": { - "vector": [0, -0.25, -1.5] - } - } - }, - "left_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "0.3": { - "vector": [5, 0, 0] - }, - "0.55": { - "vector": [15, 0, 0] - }, - "0.65": { - "vector": [15, 0, 0] - }, - "0.7": { - "vector": [15, 0, 0] - }, - "0.75": { - "vector": [15, 0, 0] - }, - "1.25": { - "vector": [5, 0, 0] - } - } - }, - "right_leg": { - "rotation": { - "0.0": { - "vector": [-2.5, 0, 0] - }, - "0.15": { - "vector": [-47.5, 0, 0] - }, - "0.4": { - "vector": [-47.5, 0, 0] - }, - "0.5": { - "vector": [-27.06, 0, 0] - }, - "1.25": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, -0.25, 0.5] - }, - "0.15": { - "vector": [0, -0.25, 0.5] - }, - "0.4": { - "vector": [0, -0.25, 0.5] - }, - "1.25": { - "vector": [0, -0.25, 0.5] - } - } - }, - "right_knee": { - "rotation": { - "0.0": { - "vector": [5, 0, 0] - }, - "0.15": { - "vector": [27.5, 0, 0] - }, - "0.4": { - "vector": [27.5, 0, 0] - }, - "0.5": { - "vector": [17.5, 0, 0] - }, - "0.65": { - "vector": [31.03, 0, 0] - }, - "1.25": { - "vector": [5, 0, 0] - } - } - }, - "h_head_furious": { - "rotation": { - "0.0": { - "vector": [0, -22.5, 0] - }, - "0.3": { - "vector": [5.24771, -55.29505, 5.70142] - }, - "0.5": { - "vector": [4.24777, -45.32881, 6.99588] - }, - "0.55": { - "vector": [2.01385, -45.16677, 0.61212] - }, - "0.6": { - "vector": [-31.34904, 12.54752, 4.35778] - }, - "0.75": { - "vector": [-37.83777, 14.32159, 7.88953] - }, - "1.25": { - "vector": [0, -22.5, 0] - } - } - }, - "inactive_axe": { - "scale": { - "vector": [0, 0, 0] - } - }, - "leg_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "h_slash10": { - "scale": { - "vector": [0, 0, 0] - } - }, - "body2": { - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.4": { - "vector": [0, 0, -1] - }, - "0.5": { - "vector": [0, -2, -7] - }, - "0.55": { - "vector": [0, -2, -9.5] - }, - "0.6": { - "vector": [0, -2, -10] - }, - "0.65": { - "vector": [0, -2, -11] - }, - "0.75": { - "vector": [0, -2, -11] - }, - "1.25": { - "vector": [0, 0, 0] - } - } - }, - "dreadnought_portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_normal": { - "scale": { - "vector": [0, 0, 0] - } - } - }, - "sound_effects": { - "0.6": { - "effect": "axe_hit" - } - } - }, - "death": { - "animation_length": 10, - "bones": { - "right_heel": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-12.5, 0, 0] - }, - "0.35": { - "vector": [17.5, 0, 0] - } - } - }, - "right_foot": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-33.13773, 0.2136, -1.9553] - }, - "0.35": { - "vector": [27.10718, 0.299, -2.7374] - }, - "0.6": { - "vector": [72.10718, 0.299, -2.7374] - } - } - }, - "left_heel": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-12.5, 0, 0] - }, - "0.35": { - "vector": [17.5, 0, 0] - } - } - }, - "left_foot": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-33.13773, -0.21358, 1.95528] - }, - "0.35": { - "vector": [27.10718, -0.29901, 2.73739] - }, - "0.6": { - "vector": [72.10718, -0.29901, 2.73739] - } - } - }, - "torso": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-35, 0, 0] - }, - "0.35": { - "vector": [-45, 0, 0] - }, - "0.45": { - "vector": [-45, 0, 0] - }, - "0.6": { - "vector": [22.5, 0, 0] - } - } - }, - "left_arm": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [-99.28275, -76.00637, 13.97749] - }, - "0.5": { - "vector": [-99.28275, -76.00637, 13.97749] - }, - "0.7": { - "vector": [-99.28275, -76.00637, 13.97749] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0] - } - } - }, - "left_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-2.5, 0, 0] - }, - "0.35": { - "vector": [12.5, 0, 0] - }, - "0.5": { - "vector": [17.5, 0, 0] - }, - "0.6": { - "vector": [17.5, 0, 0] - }, - "0.7": { - "vector": [17.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [-0.75, 0, 0] - }, - "0.5": { - "vector": [-0.75, 0, 0] - }, - "0.6": { - "vector": [0, 0, 0] - } - } - }, - "right_arm": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [-166.3778, 49.43206, -84.18841] - }, - "0.5": { - "vector": [-166.3778, 49.43206, -84.18841] - }, - "0.7": { - "vector": [-166.3778, 49.43206, -84.18841] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 0, 0] - } - } - }, - "right_elbow": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-2.5, 0, 0] - }, - "0.35": { - "vector": [12.5, 0, 0] - }, - "0.5": { - "vector": [17.5, 0, 0] - }, - "0.6": { - "vector": [17.5, 0, 0] - }, - "0.7": { - "vector": [17.5, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0.75, 0, 0] - }, - "0.5": { - "vector": [0.75, 0, 0] - }, - "0.6": { - "vector": [0, 0, 0] - } - } - }, - "axe": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.6": { - "vector": [-112.2725, 1.95838, -155.96286] - }, - "0.7": { - "vector": [-111.16188, -2.34128, -158.55673] - } - }, - "position": { - "0.6": { - "vector": [0, 0, 0] - }, - "0.7": { - "vector": [2.75, -16, -4.25] - }, - "0.75": { - "vector": [4.75, -18, -6.25] - } - } - }, - "left_leg": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, 7.5, 0] - }, - "0.6": { - "vector": [25, 7.5, 0] - } - } - }, - "left_knee": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [72.86, 0, 0] - }, - "0.35": { - "vector": [42.5, 0, 0] - }, - "0.6": { - "vector": [0, 0, 0] - } - } - }, - "right_leg": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [0, -7.5, 0] - }, - "0.6": { - "vector": [25, -7.5, 0] - } - } - }, - "right_knee": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [72.86, 0, 0] - }, - "0.35": { - "vector": [42.5, 0, 0] - }, - "0.6": { - "vector": [0, 0, 0] - } - } - }, - "h_head_furious": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [0, 0, 0] - }, - "0.4": { - "vector": [-22.5, 0, 0] - }, - "0.6": { - "vector": [-37.5, 0, 0] - }, - "0.75": { - "vector": [-12.5, 0, 0] - } - } - }, - "inactive_axe": { - "scale": { - "vector": [0, 0, 0] - } - }, - "leg_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "h_slash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "body2": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [-7.5, 0, 0] - }, - "0.35": { - "vector": [27.5, 0, 0] - }, - "0.45": { - "vector": [65, 0, 0] - }, - "0.7": { - "vector": [65, 0, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.25": { - "vector": [0, -8.5, -6] - }, - "0.35": { - "vector": [0, -10, -10] - }, - "0.45": { - "vector": [0, -19, -14] - } - } - }, - "dreadnought_portal": { - "scale": { - "vector": [0, 0, 0] - } - }, - "eyes_attack": { - "scale": { - "vector": [0, 0, 0] - } - } - } - }, - "spawn": { - "animation_length": 14.35, - "override_previous_animation": true, - "bones": { - "dreadnought": { - "position": { - "0.0": { - "vector": [0, 0, 90] - }, - "0.3": { - "vector": [0, 0, 90] - }, - "0.35": { - "vector": [0, 0, 80] - }, - "2.35": { - "vector": [0, 0, 64] - }, - "10.35": { - "vector": [0, 0, 32] - }, - "14.35": { - "vector": [0, 0, 0] - } - }, - "scale": { - "0.3": { - "vector": [0, 0, 0] - }, - "0.35": { - "vector": [1, 1, 1] - } - } - }, - "torso": { - "rotation": { - "0.35": { - "vector": [0, 0, 0] - }, - "9.35": { - "vector": [0, 0, 0] - }, - "10.35": { - "vector": [0, 22.5, 0] - }, - "12.35": { - "vector": [0, 22.5, 0] - }, - "14.35": { - "vector": [0, 22.5, 0] - } - }, - "position": { - "0.35": { - "vector": [-0.25, 0, 0] - }, - "0.8": { - "vector": [-0.33, 0, 0] - }, - "1.6": { - "vector": [0.25, 0, 0] - }, - "2.05": { - "vector": [0.33, 0, 0] - }, - "2.85": { - "vector": [-0.25, 0, 0] - }, - "3.3": { - "vector": [-0.33, 0, 0] - }, - "4.1": { - "vector": [0.25, 0, 0] - }, - "4.55": { - "vector": [0.33, 0, 0] - }, - "5.35": { - "vector": [-0.25, 0, 0] - }, - "5.8": { - "vector": [-0.33, 0, 0] - }, - "6.6": { - "vector": [0.25, 0, 0] - }, - "7.05": { - "vector": [0.33, 0, 0] - }, - "7.85": { - "vector": [-0.25, 0, 0] - }, - "8.3": { - "vector": [-0.33, 0, 0] - }, - "9.1": { - "vector": [0.25, 0, 0] - }, - "9.55": { - "vector": [0.33, 0, 0] - }, - "10.35": { - "vector": [-0.25, 0, 0] - }, - "10.65": { - "vector": [-0.33, 0, 0] - }, - "11.35": { - "vector": [0.25, 0, 0] - }, - "11.65": { - "vector": [0.33, 0, 0] - }, - "12.35": { - "vector": [-0.25, 0, 0] - }, - "12.65": { - "vector": [-0.33, 0, 0] - }, - "13.35": { - "vector": [0.25, 0, 0] - }, - "13.65": { - "vector": [0.33, 0, 0] - }, - "14.35": { - "vector": [0, 0, 0] - } - } - }, - "upper_torso": { - "rotation": { - "0.35": { - "vector": [0, 0, 1] - }, - "0.8": { - "vector": [0, 0, 2.5] - }, - "1.6": { - "vector": [0, 0, -1] - }, - "2.05": { - "vector": [0, 0, -2.5] - }, - "2.85": { - "vector": [0, 0, 1] - }, - "3.3": { - "vector": [0, 0, 2.5] - }, - "4.1": { - "vector": [0, 0, -1] - }, - "4.55": { - "vector": [0, 0, -2.5] - }, - "5.35": { - "vector": [0, 0, 1] - }, - "5.8": { - "vector": [0, 0, 2.5] - }, - "6.6": { - "vector": [0, 0, -1] - }, - "7.05": { - "vector": [0, 0, -2.5] - }, - "7.85": { - "vector": [0, 0, 1] - }, - "8.3": { - "vector": [0, 0, 2.5] - }, - "9.1": { - "vector": [0, 0, -1] - }, - "9.55": { - "vector": [0, 0, -2.5] - }, - "10.35": { - "vector": [0, 0, 1] - }, - "10.65": { - "vector": [0, 0, 2.5] - }, - "11.35": { - "vector": [0, 0, -1] - }, - "11.65": { - "vector": [0, 0, -2.5] - }, - "12.35": { - "vector": [0, 0, 1] - }, - "12.65": { - "vector": [0, 0, 2.5] - }, - "13.35": { - "vector": [0, 0, -1] - }, - "13.65": { - "vector": [0, 0, -2.5] - }, - "14.35": { - "vector": [0, 0, 0] - } - }, - "position": { - "0.35": { - "vector": [0, 0, 0] - }, - "0.5": { - "vector": [0, -0.17, 0] - }, - "0.65": { - "vector": [0, -0.83, 0] - }, - "0.8": { - "vector": [0, -1, 0] - }, - "0.95": { - "vector": [0, -0.93, 0] - }, - "1.4": { - "vector": [0, -0.07, 0] - }, - "1.6": { - "vector": [0, 0, 0] - }, - "1.75": { - "vector": [0, -0.17, 0] - }, - "1.9": { - "vector": [0, -0.83, 0] - }, - "2.05": { - "vector": [0, -1, 0] - }, - "2.2": { - "vector": [0, -0.93, 0] - }, - "2.7": { - "vector": [0, -0.07, 0] - }, - "2.85": { - "vector": [0, 0, 0] - }, - "3.0": { - "vector": [0, -0.17, 0] - }, - "3.15": { - "vector": [0, -0.83, 0] - }, - "3.3": { - "vector": [0, -1, 0] - }, - "3.45": { - "vector": [0, -0.93, 0] - }, - "3.9": { - "vector": [0, -0.07, 0] - }, - "4.1": { - "vector": [0, 0, 0] - }, - "4.25": { - "vector": [0, -0.17, 0] - }, - "4.4": { - "vector": [0, -0.83, 0] - }, - "4.55": { - "vector": [0, -1, 0] - }, - "4.7": { - "vector": [0, -0.93, 0] - }, - "5.2": { - "vector": [0, -0.07, 0] - }, - "5.35": { - "vector": [0, 0, 0] - }, - "5.5": { - "vector": [0, -0.17, 0] - }, - "5.65": { - "vector": [0, -0.83, 0] - }, - "5.8": { - "vector": [0, -1, 0] - }, - "5.95": { - "vector": [0, -0.93, 0] - }, - "6.4": { - "vector": [0, -0.07, 0] - }, - "6.6": { - "vector": [0, 0, 0] - }, - "6.75": { - "vector": [0, -0.17, 0] - }, - "6.9": { - "vector": [0, -0.83, 0] - }, - "7.05": { - "vector": [0, -1, 0] - }, - "7.2": { - "vector": [0, -0.93, 0] - }, - "7.7": { - "vector": [0, -0.07, 0] - }, - "7.85": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -0.17, 0] - }, - "8.15": { - "vector": [0, -0.83, 0] - }, - "8.3": { - "vector": [0, -1, 0] - }, - "8.45": { - "vector": [0, -0.93, 0] - }, - "8.9": { - "vector": [0, -0.07, 0] - }, - "9.1": { - "vector": [0, 0, 0] - }, - "9.25": { - "vector": [0, -0.17, 0] - }, - "9.4": { - "vector": [0, -0.83, 0] - }, - "9.55": { - "vector": [0, -1, 0] - }, - "9.7": { - "vector": [0, -0.93, 0] - }, - "10.2": { - "vector": [0, -0.07, 0] - }, - "10.35": { - "vector": [0, 0, 0] - }, - "10.45": { - "vector": [0, -0.17, 0] - }, - "10.55": { - "vector": [0, -0.83, 0] - }, - "10.65": { - "vector": [0, -1, 0] - }, - "10.8": { - "vector": [0, -0.93, 0] - }, - "11.2": { - "vector": [0, -0.07, 0] - }, - "11.35": { - "vector": [0, 0, 0] - }, - "11.45": { - "vector": [0, -0.17, 0] - }, - "11.55": { - "vector": [0, -0.83, 0] - }, - "11.65": { - "vector": [0, -1, 0] - }, - "11.8": { - "vector": [0, -0.93, 0] - }, - "12.2": { - "vector": [0, -0.07, 0] - }, - "12.35": { - "vector": [0, 0, 0] - }, - "12.45": { - "vector": [0, -0.17, 0] - }, - "12.55": { - "vector": [0, -0.83, 0] - }, - "12.65": { - "vector": [0, -1, 0] - }, - "12.8": { - "vector": [0, -0.93, 0] - }, - "13.2": { - "vector": [0, -0.07, 0] - }, - "13.35": { - "vector": [0, 0, 0] - }, - "13.45": { - "vector": [0, -0.17, 0] - }, - "13.55": { - "vector": [0, -0.83, 0] - }, - "13.65": { - "vector": [0, -1, 0] - }, - "13.8": { - "vector": [0, -0.93, 0] - }, - "14.2": { - "vector": [0, -0.07, 0] - }, - "14.35": { - "vector": [0, 0, 0] - } - } - }, - "left_arm": { - "rotation": { - "0.35": { - "vector": [-65.3438, 2.97085, -54.00316] - }, - "1.35": { - "vector": [-65.3438, 2.97085, -54.00316] - }, - "2.1": { - "vector": [-19.29063, 7.22373, -42.19927] - }, - "2.6": { - "vector": [13.14, 0.83582, -11.7567] - }, - "3.45": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "5.35": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "6.6": { - "vector": [12.5, 0, 0] - }, - "7.4": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "7.85": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "9.1": { - "vector": [12.5, 0, 0] - }, - "9.9": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "10.35": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "14.35": { - "vector": [0, 0, 0] - } - } - }, - "right_arm": { - "rotation": { - "0.35": { - "vector": [12.68664, -9.76061, -2.18558] - }, - "2.85": { - "vector": [12.68664, -9.76061, -2.18558] - }, - "3.55": { - "vector": [-10.15108, -9.84655, 1.75378] - }, - "4.1": { - "vector": [-10.08648, -8.98092, 6.66735] - }, - "5.35": { - "vector": [12.68664, -9.76061, -2.18558] - }, - "6.05": { - "vector": [-10.15108, -9.84655, 1.75378] - }, - "6.6": { - "vector": [-10.08648, -8.98092, 6.66735] - }, - "7.85": { - "vector": [12.68664, -9.76061, -2.18558] - }, - "8.2": { - "vector": [37.68664, -9.76061, -2.18558] - }, - "8.35": { - "vector": [20.18664, -9.76061, -2.18558] - }, - "9.35": { - "vector": [25.0691, 18.40276, 1.7084] - }, - "10.35": { - "vector": [36.94319, 29.52361, 9.71415] - }, - "10.65": { - "vector": [37.94, 29.52, 9.71] - }, - "11.35": { - "vector": [37.1047, 28.17926, 12.12078] - }, - "11.65": { - "vector": [36.1047, 28.17926, 12.12078] - }, - "12.35": { - "vector": [36.94319, 29.52361, 9.71415] - }, - "12.65": { - "vector": [37.94, 29.52, 9.71] - }, - "13.35": { - "vector": [37.1047, 28.17926, 12.12078] - }, - "13.65": { - "vector": [36.1047, 28.17926, 12.12078] - }, - "14.35": { - "vector": [36.94319, 29.52361, 9.71415] - } - }, - "position": { - "7.85": { - "vector": [0, 0, 0] - }, - "8.2": { - "vector": [0, 1, 1] - }, - "8.35": { - "vector": [-1, -1, -1] - }, - "9.35": { - "vector": [0, 0, 0] - }, - "10.35": { - "vector": [0, 0, 0] - }, - "10.45": { - "vector": [0, 0.17, 0] - }, - "10.55": { - "vector": [0, 0.83, 0] - }, - "10.65": { - "vector": [0, 1, 0] - }, - "10.8": { - "vector": [0, 0.93, 0] - }, - "11.2": { - "vector": [0, 0.07, 0] - }, - "11.35": { - "vector": [0, 0, 0] - }, - "11.45": { - "vector": [0, 0.17, 0] - }, - "11.55": { - "vector": [0, 0.83, 0] - }, - "11.65": { - "vector": [0, 1, 0] - }, - "11.8": { - "vector": [0, 0.93, 0] - }, - "12.2": { - "vector": [0, 0.07, 0] - }, - "12.35": { - "vector": [0, 0, 0] - }, - "12.45": { - "vector": [0, 0.17, 0] - }, - "12.55": { - "vector": [0, 0.83, 0] - }, - "12.65": { - "vector": [0, 1, 0] - }, - "12.8": { - "vector": [0, 0.93, 0] - }, - "13.2": { - "vector": [0, 0.07, 0] - }, - "13.35": { - "vector": [0, 0, 0] - }, - "13.45": { - "vector": [0, 0.17, 0] - }, - "13.55": { - "vector": [0, 0.83, 0] - }, - "13.65": { - "vector": [0, 1, 0] - }, - "13.8": { - "vector": [0, 0.93, 0] - }, - "14.2": { - "vector": [0, 0.07, 0] - }, - "14.35": { - "vector": [0, 0, 0] - } - } - }, - "right_elbow": { - "rotation": { - "0.35": { - "vector": [22.5, 0, 0] - }, - "2.85": { - "vector": [22.5, 0, 0] - }, - "5.35": { - "vector": [22.5, 0, 0] - }, - "7.85": { - "vector": [22.5, 0, 0] - }, - "8.1": { - "vector": [-2.5, 0, 0] - }, - "8.25": { - "vector": [-2.5, 0, 0] - }, - "8.4": { - "vector": [2.5, 0, 0] - }, - "9.35": { - "vector": [22.5, 0, 0] - }, - "10.35": { - "vector": [0, 0, 0] - } - } - }, - "axe": { - "rotation": { - "8.4": { - "vector": [-17.76543, -17.59014, -11.04134] - }, - "8.45": { - "vector": [-17.76543, -17.59014, -11.04134] - }, - "8.85": { - "vector": [-17.76543, -17.59014, -11.04134] - }, - "9.35": { - "vector": [-26.84362, -11.72676, -7.36089] - }, - "10.35": { - "vector": [0, 0, 0] - } - }, - "position": { - "8.35": { - "vector": [0.75, 3, 3] - }, - "8.4": { - "vector": [0.75, 3, 3] - }, - "8.45": { - "vector": [0.75, 3, 3] - }, - "8.85": { - "vector": [0.75, 3, 3] - }, - "10.35": { - "vector": [0, 0, 0] - } - }, - "scale": { - "8.35": { - "vector": [0, 0, 0] - }, - "8.4": { - "vector": [1, 1, 1] - } - } - }, - "left_leg": { - "rotation": { - "0.35": { - "vector": [5, 0, 0] - }, - "1.05": { - "vector": [-10, 0, 0] - }, - "1.6": { - "vector": [-9.96271, 0.86717, 4.92442] - }, - "2.85": { - "vector": [5, 0, 0] - }, - "3.55": { - "vector": [-10, 0, 0] - }, - "4.1": { - "vector": [-9.96271, 0.86717, 4.92442] - }, - "5.35": { - "vector": [5, 0, 0] - }, - "6.05": { - "vector": [-10, 0, 0] - }, - "6.6": { - "vector": [-9.96271, 0.86717, 4.92442] - }, - "7.85": { - "vector": [5, 0, 0] - }, - "8.55": { - "vector": [-10, 0, 0] - }, - "9.1": { - "vector": [-9.96271, 0.86717, 4.92442] - }, - "10.35": { - "vector": [12.5, 0, 0] - }, - "10.9": { - "vector": [-10, 0, 0] - }, - "11.35": { - "vector": [-9.96271, 0.86717, 4.92442] - }, - "12.35": { - "vector": [12.5, 0, 0] - }, - "12.9": { - "vector": [-10, 0, 0] - }, - "13.35": { - "vector": [-9.96271, 0.86717, 4.92442] - }, - "14.35": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.35": { - "vector": [0, -0.5, -1] - }, - "1.05": { - "vector": [0, 0, -2] - }, - "1.6": { - "vector": [0, -1, -2.5] - }, - "2.85": { - "vector": [0, -0.5, -1] - }, - "3.55": { - "vector": [0, 0, -2] - }, - "4.1": { - "vector": [0, -1, -2.5] - }, - "5.35": { - "vector": [0, -0.5, -1] - }, - "6.05": { - "vector": [0, 0, -2] - }, - "6.6": { - "vector": [0, -1, -2.5] - }, - "7.85": { - "vector": [0, -0.5, -1] - }, - "8.55": { - "vector": [0, 0, -2] - }, - "9.1": { - "vector": [0, -1, -2.5] - }, - "10.35": { - "vector": [0, -0.5, -1] - }, - "10.5": { - "vector": [0, 1, -2] - }, - "10.9": { - "vector": [0, 1, -2] - }, - "11.35": { - "vector": [0, -1, -2.5] - }, - "12.35": { - "vector": [0, -0.5, -1] - }, - "12.5": { - "vector": [0, 1, -2] - }, - "12.9": { - "vector": [0, 1, -2] - }, - "13.35": { - "vector": [0, -1, -2.5] - }, - "14.35": { - "vector": [0, -0.25, -1.5] - } - } - }, - "left_knee": { - "rotation": { - "0.35": { - "vector": [0, 0, 0] - }, - "0.8": { - "vector": [32.5, 0, 0] - }, - "1.6": { - "vector": [5, 0, 0] - }, - "2.85": { - "vector": [0, 0, 0] - }, - "3.3": { - "vector": [32.5, 0, 0] - }, - "4.1": { - "vector": [5, 0, 0] - }, - "5.35": { - "vector": [0, 0, 0] - }, - "5.8": { - "vector": [32.5, 0, 0] - }, - "6.6": { - "vector": [5, 0, 0] - }, - "7.85": { - "vector": [0, 0, 0] - }, - "8.3": { - "vector": [32.5, 0, 0] - }, - "9.1": { - "vector": [5, 0, 0] - }, - "10.35": { - "vector": [0, 0, 0] - }, - "10.65": { - "vector": [32.5, 0, 0] - }, - "11.35": { - "vector": [5, 0, 0] - }, - "12.35": { - "vector": [0, 0, 0] - }, - "12.65": { - "vector": [32.5, 0, 0] - }, - "13.35": { - "vector": [5, 0, 0] - }, - "14.35": { - "vector": [5, 0, 0] - } - } - }, - "right_leg": { - "rotation": { - "0.35": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "1.6": { - "vector": [5, 0, 0] - }, - "2.4": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "2.85": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "4.1": { - "vector": [5, 0, 0] - }, - "4.9": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "5.35": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "6.6": { - "vector": [5, 0, 0] - }, - "7.4": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "7.85": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "9.1": { - "vector": [5, 0, 0] - }, - "9.9": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "10.35": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "11.35": { - "vector": [12.5, 0, 0] - }, - "11.9": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "12.35": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "13.35": { - "vector": [12.5, 0, 0] - }, - "13.9": { - "vector": [-9.96271, -0.86717, -4.92442] - }, - "14.35": { - "vector": [-2.5, 0, 0] - } - }, - "position": { - "0.35": { - "vector": [0, -1, -0.5] - }, - "1.6": { - "vector": [0, -0.5, 1] - }, - "2.4": { - "vector": [0, 0, 0] - }, - "2.85": { - "vector": [0, -1, -0.5] - }, - "4.1": { - "vector": [0, -0.5, 1] - }, - "4.9": { - "vector": [0, 0, 0] - }, - "5.35": { - "vector": [0, -1, -0.5] - }, - "6.6": { - "vector": [0, -0.5, 1] - }, - "7.4": { - "vector": [0, 0, 0] - }, - "7.85": { - "vector": [0, -1, -0.5] - }, - "9.1": { - "vector": [0, -0.5, 1] - }, - "9.9": { - "vector": [0, 0, 0] - }, - "10.35": { - "vector": [0, -1, -0.5] - }, - "11.35": { - "vector": [0, -0.5, 1] - }, - "11.5": { - "vector": [0, 1, 0] - }, - "11.9": { - "vector": [0, 1, 0] - }, - "12.35": { - "vector": [0, -1, -0.5] - }, - "13.35": { - "vector": [0, -0.5, 1] - }, - "13.5": { - "vector": [0, 1, 0] - }, - "13.9": { - "vector": [0, 1, 0] - }, - "14.35": { - "vector": [0, -0.25, 0.5] - } - } - }, - "right_knee": { - "rotation": { - "0.35": { - "vector": [5, 0, 0] - }, - "1.6": { - "vector": [0, 0, 0] - }, - "2.05": { - "vector": [32.5, 0, 0] - }, - "2.85": { - "vector": [5, 0, 0] - }, - "4.1": { - "vector": [0, 0, 0] - }, - "4.55": { - "vector": [32.5, 0, 0] - }, - "5.35": { - "vector": [5, 0, 0] - }, - "6.6": { - "vector": [0, 0, 0] - }, - "7.05": { - "vector": [32.5, 0, 0] - }, - "7.85": { - "vector": [5, 0, 0] - }, - "9.1": { - "vector": [0, 0, 0] - }, - "9.55": { - "vector": [32.5, 0, 0] - }, - "10.35": { - "vector": [5, 0, 0] - }, - "11.35": { - "vector": [0, 0, 0] - }, - "11.65": { - "vector": [32.5, 0, 0] - }, - "12.35": { - "vector": [5, 0, 0] - }, - "13.35": { - "vector": [0, 0, 0] - }, - "13.65": { - "vector": [32.5, 0, 0] - }, - "14.35": { - "vector": [5, 0, 0] - } - } - }, - "inactive_axe": { - "rotation": { - "0.35": { - "vector": [2.51565, -47.38422, -0.7522] - }, - "2.85": { - "vector": [-32.48435, -47.38422, -0.7522] - }, - "3.55": { - "vector": [-17.48435, -47.38422, -0.7522] - }, - "4.1": { - "vector": [-19.98435, -47.38422, -0.7522] - }, - "5.35": { - "vector": [-32.48435, -47.38422, -0.7522] - }, - "6.05": { - "vector": [-17.48435, -47.38422, -0.7522] - }, - "6.6": { - "vector": [-19.98435, -47.38422, -0.7522] - }, - "7.85": { - "vector": [-17.76543, -17.59014, -11.04134] - }, - "8.15": { - "vector": [-17.76543, -17.59014, -11.04134] - }, - "8.35": { - "vector": [-17.76543, -17.59014, -11.04134] - } - }, - "position": { - "0.35": { - "vector": [0, 7, 7] - }, - "2.85": { - "vector": [0, 1, 4] - }, - "3.55": { - "vector": [0, 2, 4] - }, - "4.1": { - "vector": [0, 1, 4] - }, - "5.35": { - "vector": [0, 1, 4] - }, - "6.05": { - "vector": [-1, 2.75, 4] - }, - "6.6": { - "vector": [-1, 2, 4] - }, - "7.85": { - "vector": [0.75, 3, 3] - }, - "8.15": { - "vector": [0.75, 3, 3] - }, - "8.35": { - "vector": [0.75, 3, 3] - }, - "8.4": { - "vector": [0, -3200, 4] - } - }, - "scale": { - "0.0": { - "vector": [1, 1, 1] - }, - "8.35": { - "vector": [1, 1, 1] - }, - "8.45": { - "vector": [0, 0, 0] - } - } - }, - "h_head_furious": { - "rotation": { - "0.35": { - "vector": [0, 0, -1] - }, - "0.8": { - "vector": [15, 0, 1] - }, - "1.6": { - "vector": [15, 0, 1] - }, - "2.85": { - "vector": [0, 0, -1] - }, - "3.3": { - "vector": [0, 0, -2.5] - }, - "4.1": { - "vector": [0, 0, 1] - }, - "4.55": { - "vector": [0, 0, 2.5] - }, - "5.35": { - "vector": [0, 0, -1] - }, - "5.8": { - "vector": [0, 0, -2.5] - }, - "6.6": { - "vector": [0, 0, 1] - }, - "7.05": { - "vector": [0, 0, 2.5] - }, - "7.85": { - "vector": [0, 0, -1] - }, - "8.3": { - "vector": [0, 0, -2.5] - }, - "9.1": { - "vector": [0, 0, 1] - }, - "9.55": { - "vector": [0, 0, 2.5] - }, - "10.35": { - "vector": [0, -22.5, -1] - }, - "10.65": { - "vector": [0, -22.5, -2.5] - }, - "11.35": { - "vector": [0, -22.5, 1] - }, - "11.65": { - "vector": [0, -22.5, 2.5] - }, - "12.35": { - "vector": [0, -22.5, -1] - }, - "12.65": { - "vector": [0, -22.5, -2.5] - }, - "13.35": { - "vector": [0, -22.5, 1] - }, - "13.65": { - "vector": [0, -22.5, 2.5] - }, - "14.35": { - "vector": [0, -22.5, 0] - } - }, - "position": { - "0.35": { - "vector": [0, 0, 0] - }, - "1.35": { - "vector": [0, 0, -2] - }, - "1.75": { - "vector": [0, 0, -2] - }, - "2.05": { - "vector": [0, 0, 0] - }, - "14.35": { - "vector": [0, 0, 0] - } - } - }, - "hand_boomstick": { - "scale": { - "vector": [0, 0, 0] - } - }, - "h_slash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "body2": { - "position": { - "vector": [0, 0, 0] - } - }, - "dreadnought_portal": { - "position": { - "0.0": { - "vector": [0, 0, 69] - }, - "7.85": { - "vector": [0, 0, 69] - }, - "8.0": { - "vector": [0, 25, 69] - } - }, - "scale": { - "7.85": { - "vector": [1, 1, 1] - }, - "8.1": { - "vector": [0, 0, 0] - } - } - }, - "black": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cluster1": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud1": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud2": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud3": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud4": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster2": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud5": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud6": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud7": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud8": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud9": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster3": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud10": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud11": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud12": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud13": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster4": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud14": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud15": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud16": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud17": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud18": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster5": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud19": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud20": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud21": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud22": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster6": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud23": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud24": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud25": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud26": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud27": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster7": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud28": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud29": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud30": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud31": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster8": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud32": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud33": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud34": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud35": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud36": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster9": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud37": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud38": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud39": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud40": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster10": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud41": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud42": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud43": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud44": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud45": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster11": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud46": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud47": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud48": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud49": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster12": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud50": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud51": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud52": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud53": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud54": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster13": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud55": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud56": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud57": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud58": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster14": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud59": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud60": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud61": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud62": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud63": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster15": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud64": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud65": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud66": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud67": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster16": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud68": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud69": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud70": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud71": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud72": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster17": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud73": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud74": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud75": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud76": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster18": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud77": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud78": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud79": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud80": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud81": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster19": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud82": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud83": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud84": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud85": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster20": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud86": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud87": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud88": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud89": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud90": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster21": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud91": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud92": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud93": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud94": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster22": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud95": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud96": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud97": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud98": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud99": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster23": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud100": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud101": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud102": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud103": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster24": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud104": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud105": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud106": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud107": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud108": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster25": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud109": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud110": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud111": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud112": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster26": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud113": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud114": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud115": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud116": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud117": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster27": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud118": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud119": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud120": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud121": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster28": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud122": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud123": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud124": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud125": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud126": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster29": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud127": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud128": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud129": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud130": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster30": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud131": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud132": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud133": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud134": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud135": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cluster31": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud136": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 360, 0] - } - } - }, - "cloud137": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud138": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 960, 0] - } - } - }, - "cloud139": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 2880, 0] - } - } - }, - "cluster32": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 0, -360] - } - } - }, - "cloud140": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -2880, 0] - } - } - }, - "cloud141": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud142": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, 1440, 0] - } - } - }, - "cloud143": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "cloud144": { - "rotation": { - "0.0": { - "vector": [0, 0, 0] - }, - "8.0": { - "vector": [0, -1440, 0] - } - } - }, - "eyes_attack": { - "scale": { - "vector": [0, 0, 0] - } - } - }, - "sound_effects": { - "0.0": { - "effect": "portal" - }, - "0.35": { - "effect": "walk" - }, - "1.7": { - "effect": "walk" - }, - "2.85": { - "effect": "walk" - }, - "4.05": { - "effect": "walk" - }, - "4.3": { - "effect": "portal" - }, - "5.25": { - "effect": "walk" - }, - "6.5": { - "effect": "walk" - }, - "7.85": { - "effect": "walk" - }, - "8.35": { - "effect": "axe" - }, - "9.05": { - "effect": "walk" - }, - "10.3": { - "effect": "walk" - }, - "11.2": { - "effect": "walk" - }, - "12.25": { - "effect": "walk" - }, - "13.25": { - "effect": "walk" - }, - "14.0": { - "effect": "walk" - } - } - } - }, - "azurelib_format_version": 2 -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/animations/item/doomicorn.animation.json b/common/src/main/resources/assets/azurelib/animations/item/doomicorn.animation.json deleted file mode 100644 index c7d87a8e1..000000000 --- a/common/src/main/resources/assets/azurelib/animations/item/doomicorn.animation.json +++ /dev/null @@ -1,741 +0,0 @@ -{ - "format_version": "1.8.0", - "animations": { - "equipping": { - "loop": true, - "animation_length": 1.84, - "bones": { - "bipedHead": { - "rotation": { - "0.0": { - "vector": [ - -90, - 0, - 0 - ] - }, - "0.76": { - "vector": [ - -78.65, - 0, - 0 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "position": { - "0.0": { - "vector": [ - 0, - 0, - 2 - ] - }, - "0.76": { - "vector": [ - 0, - 10, - 15.03 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "scale": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "1.56": { - "vector": [ - 1, - 1, - 1 - ], - "easing": "easeInOutSine" - } - } - }, - "bipedBody": { - "rotation": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "0.76": { - "vector": [ - 0, - 359, - 0 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "position": { - "0.0": { - "vector": [ - 0, - 0, - 2 - ] - }, - "0.76": { - "vector": [ - 0, - 10, - 15.03 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "scale": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "1.56": { - "vector": [ - 1, - 1, - 1 - ], - "easing": "easeInOutSine" - } - } - }, - "bipedRightArm": { - "rotation": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "0.76": { - "vector": [ - 0, - 359, - 0 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "position": { - "0.0": { - "vector": [ - 0, - 0, - 2 - ] - }, - "0.76": { - "vector": [ - -15, - 10, - -1.97 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "scale": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "1.56": { - "vector": [ - 1, - 1, - 1 - ], - "easing": "easeInOutSine" - } - } - }, - "bipedLeftArm": { - "rotation": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "0.76": { - "vector": [ - 0, - 359, - 0 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "position": { - "0.0": { - "vector": [ - 0, - 0, - 2 - ] - }, - "0.76": { - "vector": [ - 15, - 10, - -1.97 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "scale": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "1.56": { - "vector": [ - 1, - 1, - 1 - ], - "easing": "easeInOutSine" - } - } - }, - "bipedRightLeg": { - "rotation": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "0.76": { - "vector": [ - 0, - 359, - 0 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "position": { - "0.0": { - "vector": [ - 0, - 0, - 2 - ] - }, - "0.76": { - "vector": [ - 11, - -31, - 15.03 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "scale": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "1.56": { - "vector": [ - 1, - 1, - 1 - ], - "easing": "easeInOutSine" - } - } - }, - "bipedLeftLeg": { - "rotation": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "0.76": { - "vector": [ - 0, - 359, - 0 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "position": { - "0.0": { - "vector": [ - 0, - 0, - 2 - ] - }, - "0.76": { - "vector": [ - -11, - -31, - 15.03 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "scale": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "1.56": { - "vector": [ - 1, - 1, - 1 - ], - "easing": "easeInOutSine" - } - } - } - } - }, - "idle": { - "loop": true, - "animation_length": 4.6, - "bones": { - "group": { - "rotation": { - "0.0": { - "vector": [ - 0, - -20, - 0 - ] - }, - "0.8": { - "vector": [ - 0, - -15, - 15 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - -20, - 0 - ], - "easing": "easeInOutSine" - }, - "2.32": { - "vector": [ - 0, - -15, - 15 - ], - "easing": "easeInOutSine" - }, - "3.08": { - "vector": [ - 0, - -20, - 0 - ], - "easing": "easeInOutSine" - }, - "3.84": { - "vector": [ - 0, - -15, - 15 - ], - "easing": "easeInOutSine" - }, - "4.6": { - "vector": [ - 0, - -20, - 0 - ], - "easing": "easeInOutSine" - } - } - }, - "group2": { - "rotation": { - "0.0": { - "vector": [ - 0, - -20, - 0 - ] - }, - "0.8": { - "vector": [ - 0, - -15, - -15 - ], - "easing": "easeInOutSine" - }, - "1.56": { - "vector": [ - 0, - -20, - 0 - ], - "easing": "easeInOutSine" - }, - "2.32": { - "vector": [ - 0, - -15, - -15 - ], - "easing": "easeInOutSine" - }, - "3.08": { - "vector": [ - 0, - -20, - 0 - ], - "easing": "easeInOutSine" - }, - "3.84": { - "vector": [ - 0, - -15, - -15 - ], - "easing": "easeInOutSine" - }, - "4.6": { - "vector": [ - 0, - -20, - 0 - ], - "easing": "easeInOutSine" - } - } - }, - "launcher": { - "rotation": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "0.52": { - "vector": [ - 25, - 0, - 0 - ], - "easing": "easeInOutSine" - }, - "4.04": { - "vector": [ - 25, - 0, - 0 - ], - "easing": "easeInOutSine" - }, - "4.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - } - }, - "bone4": { - "rotation": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "0.52": { - "vector": [ - -25, - 0, - 0 - ], - "easing": "easeInOutSine" - }, - "1.0": { - "vector": [ - -25, - -17.5, - 0 - ], - "easing": "easeInOutSine" - }, - "1.52": { - "vector": [ - -25, - 0, - 0 - ], - "easing": "easeInOutSine" - }, - "2.0": { - "vector": [ - -25, - 13, - 0 - ], - "easing": "easeInOutSine" - }, - "2.52": { - "vector": [ - -25, - 0, - 0 - ], - "easing": "easeInOutSine" - }, - "3.04": { - "vector": [ - -25, - -17.5, - 0 - ], - "easing": "easeInOutSine" - }, - "3.52": { - "vector": [ - -25, - 0, - 0 - ], - "easing": "easeInOutSine" - }, - "4.08": { - "vector": [ - -25, - 0, - 0 - ], - "easing": "easeInOutSine" - }, - "4.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - } - } - } - }, - "attacking": { - "animation_length": 2.08, - "bones": { - "blade": { - "position": { - "0.0": { - "vector": [ - 5, - 0, - 0 - ] - }, - "0.52": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutCirc" - }, - "1.56": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutCirc" - }, - "2.08": { - "vector": [ - 5, - 0, - 0 - ], - "easing": "easeInOutCirc" - } - }, - "scale": { - "0.0": { - "vector": [ - 0.7, - 0.7, - 0.7 - ] - }, - "0.52": { - "vector": [ - 1, - 1, - 1 - ], - "easing": "easeInOutCirc" - }, - "1.56": { - "vector": [ - 1, - 1, - 1 - ], - "easing": "easeInOutCirc" - }, - "2.08": { - "vector": [ - 0.7, - 0.7, - 0.7 - ], - "easing": "easeInOutCirc" - } - } - } - } - } - }, - "geckolib_format_version": 2 -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/animations/item/pistol.animation.json b/common/src/main/resources/assets/azurelib/animations/item/pistol.animation.json deleted file mode 100644 index dba595e89..000000000 --- a/common/src/main/resources/assets/azurelib/animations/item/pistol.animation.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "format_version": "1.8.0", - "animations": { - "firing": { - "loop": true, - "animation_length": 0.16, - "bones": { - "group": { - "rotation": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "0.08": { - "vector": [ - -7.5, - 0, - 0 - ], - "easing": "easeInOutSine" - }, - "0.16": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - }, - "position": { - "0.0": { - "vector": [ - 0, - 0, - 0 - ] - }, - "0.08": { - "vector": [ - 0, - 0, - 2 - ], - "easing": "easeInOutSine" - }, - "0.16": { - "vector": [ - 0, - 0, - 0 - ], - "easing": "easeInOutSine" - } - } - }, - "bone": { - "position": { - "vector": [ - 0, - 0, - -0.3 - ] - }, - "scale": { - "vector": [ - 7, - 7, - 7 - ] - } - } - } - } - }, - "geckolib_format_version": 2 -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/geo/block/stargate.geo.json b/common/src/main/resources/assets/azurelib/geo/block/stargate.geo.json deleted file mode 100644 index 8da51fbc9..000000000 --- a/common/src/main/resources/assets/azurelib/geo/block/stargate.geo.json +++ /dev/null @@ -1,877 +0,0 @@ -{ - "format_version": "1.21.20", - "minecraft:geometry": [ - { - "description": { - "identifier": "geometry.unknown", - "texture_width": 256, - "texture_height": 256, - "visible_bounds_width": 7, - "visible_bounds_height": 6.5, - "visible_bounds_offset": [0, 2.75, 0] - }, - "bones": [ - { - "name": "bone9", - "pivot": [0, 12, -0.6] - }, - { - "name": "outter_ring", - "parent": "bone9", - "pivot": [0, 40, 0], - "cubes": [ - { - "origin": [-7.95649, 0, -3], - "size": [15.91299, 4, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, -45], - "uv": { - "north": {"uv": [160, 24], "uv_size": [16, 4]}, - "east": {"uv": [42, 140], "uv_size": [6, 4]}, - "south": {"uv": [160, 28], "uv_size": [16, 4]}, - "west": {"uv": [122, 161], "uv_size": [6, 4]}, - "up": {"uv": [26, 132], "uv_size": [16, 6]}, - "down": {"uv": [26, 144], "uv_size": [16, -6]} - } - }, - { - "origin": [-7.95649, 0, -3], - "size": [15.91299, 4, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [160, 32], "uv_size": [16, 4]}, - "east": {"uv": [198, 151], "uv_size": [6, 4]}, - "south": {"uv": [160, 36], "uv_size": [16, 4]}, - "west": {"uv": [199, 50], "uv_size": [6, 4]}, - "up": {"uv": [143, 143], "uv_size": [16, 6]}, - "down": {"uv": [144, 6], "uv_size": [16, -6]} - } - }, - { - "origin": [-7.95649, 0, -3], - "size": [15.91299, 4, 6], - "uv": { - "north": {"uv": [160, 40], "uv_size": [16, 4]}, - "east": {"uv": [199, 54], "uv_size": [6, 4]}, - "south": {"uv": [160, 44], "uv_size": [16, 4]}, - "west": {"uv": [155, 200], "uv_size": [6, 4]}, - "up": {"uv": [144, 6], "uv_size": [16, 6]}, - "down": {"uv": [144, 18], "uv_size": [16, -6]} - } - }, - { - "origin": [-7.95649, 0, -3], - "size": [15.91299, 4, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [160, 48], "uv_size": [16, 4]}, - "east": {"uv": [161, 200], "uv_size": [6, 4]}, - "south": {"uv": [160, 52], "uv_size": [16, 4]}, - "west": {"uv": [167, 200], "uv_size": [6, 4]}, - "up": {"uv": [144, 18], "uv_size": [16, 6]}, - "down": {"uv": [144, 30], "uv_size": [16, -6]} - } - }, - { - "origin": [-7.95649, 0, -3], - "size": [15.91299, 4, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, 45], - "uv": { - "north": {"uv": [160, 56], "uv_size": [16, 4]}, - "east": {"uv": [173, 200], "uv_size": [6, 4]}, - "south": {"uv": [143, 160], "uv_size": [16, 4]}, - "west": {"uv": [179, 200], "uv_size": [6, 4]}, - "up": {"uv": [26, 144], "uv_size": [16, 6]}, - "down": {"uv": [144, 36], "uv_size": [16, -6]} - } - }, - { - "origin": [-7.95649, 76, -3], - "size": [15.91299, 4, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, -45], - "uv": { - "north": {"uv": [159, 160], "uv_size": [16, 4]}, - "east": {"uv": [82, 201], "uv_size": [6, 4]}, - "south": {"uv": [143, 164], "uv_size": [16, 4]}, - "west": {"uv": [201, 166], "uv_size": [6, 4]}, - "up": {"uv": [144, 36], "uv_size": [16, 6]}, - "down": {"uv": [144, 48], "uv_size": [16, -6]} - } - }, - { - "origin": [-7.95649, 76, -3], - "size": [15.91299, 4, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [159, 164], "uv_size": [16, 4]}, - "east": {"uv": [201, 170], "uv_size": [6, 4]}, - "south": {"uv": [26, 167], "uv_size": [16, 4]}, - "west": {"uv": [201, 174], "uv_size": [6, 4]}, - "up": {"uv": [144, 48], "uv_size": [16, 6]}, - "down": {"uv": [144, 60], "uv_size": [16, -6]} - } - }, - { - "origin": [-7.95649, 76, -3], - "size": [15.91299, 4, 6], - "uv": { - "north": {"uv": [42, 167], "uv_size": [16, 4]}, - "east": {"uv": [201, 178], "uv_size": [6, 4]}, - "south": {"uv": [58, 167], "uv_size": [16, 4]}, - "west": {"uv": [202, 0], "uv_size": [6, 4]}, - "up": {"uv": [42, 145], "uv_size": [16, 6]}, - "down": {"uv": [58, 151], "uv_size": [16, -6]} - } - }, - { - "origin": [-7.95649, 76, -3], - "size": [15.91299, 4, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [74, 167], "uv_size": [16, 4]}, - "east": {"uv": [202, 4], "uv_size": [6, 4]}, - "south": {"uv": [110, 167], "uv_size": [16, 4]}, - "west": {"uv": [24, 202], "uv_size": [6, 4]}, - "up": {"uv": [74, 145], "uv_size": [16, 6]}, - "down": {"uv": [90, 151], "uv_size": [16, -6]} - } - }, - { - "origin": [-7.95649, 76, -3], - "size": [15.91299, 4, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, 45], - "uv": { - "north": {"uv": [167, 145], "uv_size": [16, 4]}, - "east": {"uv": [30, 202], "uv_size": [6, 4]}, - "south": {"uv": [167, 149], "uv_size": [16, 4]}, - "west": {"uv": [36, 202], "uv_size": [6, 4]}, - "up": {"uv": [106, 145], "uv_size": [16, 6]}, - "down": {"uv": [143, 155], "uv_size": [16, -6]} - } - }, - { - "origin": [36, 32.04351, -3], - "size": [4, 15.91299, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [118, 151], "uv_size": [4, 16]}, - "east": {"uv": [122, 145], "uv_size": [6, 16]}, - "south": {"uv": [168, 0], "uv_size": [4, 16]}, - "west": {"uv": [26, 150], "uv_size": [6, 16]}, - "up": {"uv": [126, 65], "uv_size": [4, 6]}, - "down": {"uv": [126, 77], "uv_size": [4, -6]} - } - }, - { - "origin": [36, 32.04351, -3], - "size": [4, 15.91299, 6], - "uv": { - "north": {"uv": [143, 168], "uv_size": [4, 16]}, - "east": {"uv": [32, 150], "uv_size": [6, 16]}, - "south": {"uv": [147, 168], "uv_size": [4, 16]}, - "west": {"uv": [38, 151], "uv_size": [6, 16]}, - "up": {"uv": [138, 197], "uv_size": [4, 6]}, - "down": {"uv": [88, 207], "uv_size": [4, -6]} - } - }, - { - "origin": [36, 32.04351, -3], - "size": [4, 15.91299, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [151, 168], "uv_size": [4, 16]}, - "east": {"uv": [44, 151], "uv_size": [6, 16]}, - "south": {"uv": [155, 168], "uv_size": [4, 16]}, - "west": {"uv": [50, 151], "uv_size": [6, 16]}, - "up": {"uv": [42, 202], "uv_size": [4, 6]}, - "down": {"uv": [46, 208], "uv_size": [4, -6]} - } - }, - { - "origin": [-40, 32.04351, -3], - "size": [4, 15.91299, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [159, 168], "uv_size": [4, 16]}, - "east": {"uv": [56, 151], "uv_size": [6, 16]}, - "south": {"uv": [163, 168], "uv_size": [4, 16]}, - "west": {"uv": [62, 151], "uv_size": [6, 16]}, - "up": {"uv": [50, 202], "uv_size": [4, 6]}, - "down": {"uv": [54, 208], "uv_size": [4, -6]} - } - }, - { - "origin": [-40, 32.04351, -3], - "size": [4, 15.91299, 6], - "uv": { - "north": {"uv": [167, 168], "uv_size": [4, 16]}, - "east": {"uv": [68, 151], "uv_size": [6, 16]}, - "south": {"uv": [26, 171], "uv_size": [4, 16]}, - "west": {"uv": [74, 151], "uv_size": [6, 16]}, - "up": {"uv": [58, 202], "uv_size": [4, 6]}, - "down": {"uv": [62, 208], "uv_size": [4, -6]} - } - }, - { - "origin": [-40, 32.04351, -3], - "size": [4, 15.91299, 6], - "pivot": [0, 40, -1], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [30, 171], "uv_size": [4, 16]}, - "east": {"uv": [80, 151], "uv_size": [6, 16]}, - "south": {"uv": [34, 171], "uv_size": [4, 16]}, - "west": {"uv": [86, 151], "uv_size": [6, 16]}, - "up": {"uv": [66, 202], "uv_size": [4, 6]}, - "down": {"uv": [70, 208], "uv_size": [4, -6]} - } - } - ] - }, - { - "name": "bone", - "parent": "outter_ring", - "pivot": [0, 27, 0], - "cubes": [ - { - "origin": [10.4, 3.2, -3.8], - "size": [8, 6, 7.5], - "pivot": [18.4, 5.7, -3.8], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [191, 51], "uv_size": [8, 6]}, - "east": {"uv": [155, 194], "uv_size": [8, 6]}, - "south": {"uv": [163, 194], "uv_size": [8, 6]}, - "west": {"uv": [171, 194], "uv_size": [8, 6]}, - "up": {"uv": [160, 8], "uv_size": [8, 8]}, - "down": {"uv": [160, 24], "uv_size": [8, -8]} - } - }, - { - "origin": [-18.4, 3.2, -3.8], - "size": [8, 6, 7.5], - "pivot": [-18.4, 5.7, -3.8], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [199, 14], "uv_size": [8, 6]}, - "east": {"uv": [199, 20], "uv_size": [8, 6]}, - "south": {"uv": [199, 26], "uv_size": [8, 6]}, - "west": {"uv": [199, 32], "uv_size": [8, 6]}, - "up": {"uv": [78, 171], "uv_size": [8, 8]}, - "down": {"uv": [110, 179], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "bone2", - "parent": "bone", - "pivot": [-0.5, 76.8, -3.8], - "cubes": [ - { - "origin": [-4.5, 74.6, -3.8], - "size": [8, 6, 7.5], - "uv": { - "north": {"uv": [118, 73], "uv_size": [8, 6]}, - "east": {"uv": [199, 38], "uv_size": [7, 6]}, - "south": {"uv": [155, 188], "uv_size": [8, 6]}, - "west": {"uv": [199, 44], "uv_size": [7, 6]}, - "up": {"uv": [118, 171], "uv_size": [8, 7]}, - "down": {"uv": [187, 15], "uv_size": [8, -7]} - } - } - ] - }, - { - "name": "bone3", - "parent": "bone", - "pivot": [7.5, 47, -2.1], - "cubes": [ - { - "origin": [19.44583, 63.56781, -3.8], - "size": [8, 6, 7.5], - "pivot": [26.5, 64.6, -3.9], - "rotation": [0, 0, 42.5], - "uv": { - "north": {"uv": [163, 188], "uv_size": [8, 6]}, - "east": {"uv": [171, 188], "uv_size": [8, 6]}, - "south": {"uv": [179, 188], "uv_size": [8, 6]}, - "west": {"uv": [188, 184], "uv_size": [8, 6]}, - "up": {"uv": [118, 65], "uv_size": [8, 8]}, - "down": {"uv": [42, 140], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "bone4", - "parent": "bone", - "pivot": [10.8, 42.2, -2.1], - "cubes": [ - { - "origin": [27, 46.7, -3.8], - "size": [8, 6, 7.5], - "pivot": [35, 46.7, -3.8], - "rotation": [0, 0, 70], - "uv": { - "north": {"uv": [190, 160], "uv_size": [8, 6]}, - "east": {"uv": [187, 190], "uv_size": [8, 6]}, - "south": {"uv": [191, 15], "uv_size": [8, 6]}, - "west": {"uv": [191, 21], "uv_size": [8, 6]}, - "up": {"uv": [110, 151], "uv_size": [8, 8]}, - "down": {"uv": [110, 167], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "bone5", - "parent": "bone", - "pivot": [35.2, 22.1, -2.1], - "cubes": [ - { - "origin": [26.4, 20.7, -3.8], - "size": [8, 6, 7.5], - "pivot": [34.4, 23.2, -3.8], - "rotation": [0, 0, -60], - "uv": { - "north": {"uv": [191, 27], "uv_size": [8, 6]}, - "east": {"uv": [191, 33], "uv_size": [8, 6]}, - "south": {"uv": [191, 39], "uv_size": [8, 6]}, - "west": {"uv": [191, 45], "uv_size": [8, 6]}, - "up": {"uv": [159, 145], "uv_size": [8, 8]}, - "down": {"uv": [160, 8], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "bone6", - "parent": "bone", - "pivot": [-29.9, 22.8, -2.1], - "cubes": [ - { - "origin": [-34.4, 20.7, -3.8], - "size": [8, 6, 7.5], - "pivot": [-34.4, 23.2, -3.8], - "rotation": [0, 0, 60], - "uv": { - "north": {"uv": [8, 198], "uv_size": [8, 6]}, - "east": {"uv": [16, 198], "uv_size": [8, 6]}, - "south": {"uv": [198, 145], "uv_size": [8, 6]}, - "west": {"uv": [198, 160], "uv_size": [8, 6]}, - "up": {"uv": [62, 171], "uv_size": [8, 8]}, - "down": {"uv": [70, 179], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "bone7", - "parent": "bone", - "pivot": [-34.6, 48.8, -2.1], - "cubes": [ - { - "origin": [-35, 46.7, -3.8], - "size": [8, 6, 7.5], - "pivot": [-35, 46.7, -3.8], - "rotation": [0, 0, -70], - "uv": { - "north": {"uv": [187, 196], "uv_size": [8, 6]}, - "east": {"uv": [195, 196], "uv_size": [8, 6]}, - "south": {"uv": [130, 197], "uv_size": [8, 6]}, - "west": {"uv": [0, 198], "uv_size": [8, 6]}, - "up": {"uv": [46, 171], "uv_size": [8, 8]}, - "down": {"uv": [54, 179], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "bone8", - "parent": "bone", - "pivot": [-23.5, 67.1, -2.1], - "cubes": [ - { - "origin": [-28.44583, 66.56781, -3.8], - "size": [8, 6, 7.5], - "pivot": [-23.5, 67.6, -3.9], - "rotation": [0, 0, -42.5], - "uv": { - "north": {"uv": [179, 194], "uv_size": [8, 6]}, - "east": {"uv": [195, 8], "uv_size": [8, 6]}, - "south": {"uv": [195, 190], "uv_size": [8, 6]}, - "west": {"uv": [196, 184], "uv_size": [8, 6]}, - "up": {"uv": [168, 16], "uv_size": [8, 8]}, - "down": {"uv": [38, 179], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "inner_ring", - "parent": "bone9", - "pivot": [0, 40, 0], - "cubes": [ - { - "origin": [-7.35976, 3, -2], - "size": [14.71952, 4, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, -45], - "uv": { - "north": {"uv": [171, 168], "uv_size": [15, 4]}, - "east": {"uv": [48, 140], "uv_size": [4, 4]}, - "south": {"uv": [172, 0], "uv_size": [15, 4]}, - "west": {"uv": [74, 202], "uv_size": [4, 4]}, - "up": {"uv": [172, 4], "uv_size": [15, 4]}, - "down": {"uv": [172, 12], "uv_size": [15, -4]} - } - }, - { - "origin": [-7.35976, 3, -2], - "size": [14.71952, 4, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [172, 12], "uv_size": [15, 4]}, - "east": {"uv": [78, 202], "uv_size": [4, 4]}, - "south": {"uv": [171, 172], "uv_size": [15, 4]}, - "west": {"uv": [110, 202], "uv_size": [4, 4]}, - "up": {"uv": [175, 160], "uv_size": [15, 4]}, - "down": {"uv": [175, 168], "uv_size": [15, -4]} - } - }, - { - "origin": [-7.35976, 3, -2], - "size": [14.71952, 4, 4], - "uv": { - "north": {"uv": [176, 16], "uv_size": [15, 4]}, - "east": {"uv": [114, 202], "uv_size": [4, 4]}, - "south": {"uv": [176, 20], "uv_size": [15, 4]}, - "west": {"uv": [118, 202], "uv_size": [4, 4]}, - "up": {"uv": [176, 24], "uv_size": [15, 4]}, - "down": {"uv": [176, 32], "uv_size": [15, -4]} - } - }, - { - "origin": [-7.35976, 3, -2], - "size": [14.71952, 4, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [176, 32], "uv_size": [15, 4]}, - "east": {"uv": [122, 202], "uv_size": [4, 4]}, - "south": {"uv": [176, 36], "uv_size": [15, 4]}, - "west": {"uv": [185, 202], "uv_size": [4, 4]}, - "up": {"uv": [176, 40], "uv_size": [15, 4]}, - "down": {"uv": [176, 48], "uv_size": [15, -4]} - } - }, - { - "origin": [-7.35976, 3, -2], - "size": [14.71952, 4, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, 45], - "uv": { - "north": {"uv": [176, 48], "uv_size": [15, 4]}, - "east": {"uv": [189, 202], "uv_size": [4, 4]}, - "south": {"uv": [176, 52], "uv_size": [15, 4]}, - "west": {"uv": [193, 202], "uv_size": [4, 4]}, - "up": {"uv": [176, 56], "uv_size": [15, 4]}, - "down": {"uv": [171, 180], "uv_size": [15, -4]} - } - }, - { - "origin": [-7.35976, 73, -2], - "size": [14.71952, 4, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, -45], - "uv": { - "north": {"uv": [38, 179], "uv_size": [15, 4]}, - "east": {"uv": [197, 202], "uv_size": [4, 4]}, - "south": {"uv": [53, 179], "uv_size": [15, 4]}, - "west": {"uv": [201, 202], "uv_size": [4, 4]}, - "up": {"uv": [68, 179], "uv_size": [15, 4]}, - "down": {"uv": [110, 183], "uv_size": [15, -4]} - } - }, - { - "origin": [-7.35976, 73, -2], - "size": [14.71952, 4, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [171, 180], "uv_size": [15, 4]}, - "east": {"uv": [203, 8], "uv_size": [4, 4]}, - "south": {"uv": [38, 183], "uv_size": [15, 4]}, - "west": {"uv": [130, 203], "uv_size": [4, 4]}, - "up": {"uv": [53, 183], "uv_size": [15, 4]}, - "down": {"uv": [68, 187], "uv_size": [15, -4]} - } - }, - { - "origin": [-7.35976, 73, -2], - "size": [14.71952, 4, 4], - "uv": { - "north": {"uv": [110, 183], "uv_size": [15, 4]}, - "east": {"uv": [134, 203], "uv_size": [4, 4]}, - "south": {"uv": [183, 145], "uv_size": [15, 4]}, - "west": {"uv": [138, 203], "uv_size": [4, 4]}, - "up": {"uv": [183, 149], "uv_size": [15, 4]}, - "down": {"uv": [143, 188], "uv_size": [15, -4]} - } - }, - { - "origin": [-7.35976, 73, -2], - "size": [14.71952, 4, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [158, 184], "uv_size": [15, 4]}, - "east": {"uv": [142, 203], "uv_size": [4, 4]}, - "south": {"uv": [173, 184], "uv_size": [15, 4]}, - "west": {"uv": [146, 203], "uv_size": [4, 4]}, - "up": {"uv": [186, 168], "uv_size": [15, 4]}, - "down": {"uv": [186, 176], "uv_size": [15, -4]} - } - }, - { - "origin": [-7.35976, 73, -2], - "size": [14.71952, 4, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, 45], - "uv": { - "north": {"uv": [186, 176], "uv_size": [15, 4]}, - "east": {"uv": [150, 203], "uv_size": [4, 4]}, - "south": {"uv": [186, 180], "uv_size": [15, 4]}, - "west": {"uv": [203, 190], "uv_size": [4, 4]}, - "up": {"uv": [187, 0], "uv_size": [15, 4]}, - "down": {"uv": [187, 8], "uv_size": [15, -4]} - } - }, - { - "origin": [33, 32.64024, -2], - "size": [4, 14.71952, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [86, 171], "uv_size": [4, 15]}, - "east": {"uv": [83, 186], "uv_size": [4, 15]}, - "south": {"uv": [87, 186], "uv_size": [4, 15]}, - "west": {"uv": [26, 187], "uv_size": [4, 15]}, - "up": {"uv": [203, 194], "uv_size": [4, 4]}, - "down": {"uv": [203, 202], "uv_size": [4, -4]} - } - }, - { - "origin": [33, 32.64024, -2], - "size": [4, 14.71952, 4], - "uv": { - "north": {"uv": [30, 187], "uv_size": [4, 15]}, - "east": {"uv": [34, 187], "uv_size": [4, 15]}, - "south": {"uv": [38, 187], "uv_size": [4, 15]}, - "west": {"uv": [42, 187], "uv_size": [4, 15]}, - "up": {"uv": [0, 204], "uv_size": [4, 4]}, - "down": {"uv": [4, 208], "uv_size": [4, -4]} - } - }, - { - "origin": [33, 32.64024, -2], - "size": [4, 14.71952, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [46, 187], "uv_size": [4, 15]}, - "east": {"uv": [50, 187], "uv_size": [4, 15]}, - "south": {"uv": [54, 187], "uv_size": [4, 15]}, - "west": {"uv": [58, 187], "uv_size": [4, 15]}, - "up": {"uv": [8, 204], "uv_size": [4, 4]}, - "down": {"uv": [12, 208], "uv_size": [4, -4]} - } - }, - { - "origin": [-37, 32.64024, -2], - "size": [4, 14.71952, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [62, 187], "uv_size": [4, 15]}, - "east": {"uv": [66, 187], "uv_size": [4, 15]}, - "south": {"uv": [70, 187], "uv_size": [4, 15]}, - "west": {"uv": [74, 187], "uv_size": [4, 15]}, - "up": {"uv": [16, 204], "uv_size": [4, 4]}, - "down": {"uv": [20, 208], "uv_size": [4, -4]} - } - }, - { - "origin": [-37, 32.64024, -2], - "size": [4, 14.71952, 4], - "uv": { - "north": {"uv": [78, 187], "uv_size": [4, 15]}, - "east": {"uv": [110, 187], "uv_size": [4, 15]}, - "south": {"uv": [114, 187], "uv_size": [4, 15]}, - "west": {"uv": [118, 187], "uv_size": [4, 15]}, - "up": {"uv": [204, 151], "uv_size": [4, 4]}, - "down": {"uv": [154, 208], "uv_size": [4, -4]} - } - }, - { - "origin": [-37, 32.64024, -2], - "size": [4, 14.71952, 4], - "pivot": [0, 40, 0], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [122, 187], "uv_size": [4, 15]}, - "east": {"uv": [143, 188], "uv_size": [4, 15]}, - "south": {"uv": [147, 188], "uv_size": [4, 15]}, - "west": {"uv": [151, 188], "uv_size": [4, 15]}, - "up": {"uv": [158, 204], "uv_size": [4, 4]}, - "down": {"uv": [162, 208], "uv_size": [4, -4]} - } - } - ] - }, - { - "name": "portal", - "parent": "bone9", - "pivot": [0, 40, -0.7], - "cubes": [ - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, -45], - "uv": { - "north": {"uv": [52, 79], "uv_size": [13, 66]}, - "east": {"uv": [100, 151], "uv_size": [1, 66]}, - "south": {"uv": [65, 79], "uv_size": [13, 66]}, - "west": {"uv": [101, 151], "uv_size": [1, 66]}, - "up": {"uv": [191, 59], "uv_size": [13, 1]}, - "down": {"uv": [201, 183], "uv_size": [13, -1]} - } - }, - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [78, 79], "uv_size": [13, 66]}, - "east": {"uv": [102, 151], "uv_size": [1, 66]}, - "south": {"uv": [91, 79], "uv_size": [13, 66]}, - "west": {"uv": [103, 151], "uv_size": [1, 66]}, - "up": {"uv": [201, 183], "uv_size": [13, 1]}, - "down": {"uv": [203, 13], "uv_size": [13, -1]} - } - }, - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "uv": { - "north": {"uv": [104, 79], "uv_size": [13, 66]}, - "east": {"uv": [104, 151], "uv_size": [1, 66]}, - "south": {"uv": [117, 79], "uv_size": [13, 66]}, - "west": {"uv": [105, 151], "uv_size": [1, 66]}, - "up": {"uv": [203, 13], "uv_size": [13, 1]}, - "down": {"uv": [204, 59], "uv_size": [13, -1]} - } - }, - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [130, 65], "uv_size": [13, 66]}, - "east": {"uv": [106, 151], "uv_size": [1, 66]}, - "south": {"uv": [130, 131], "uv_size": [13, 66]}, - "west": {"uv": [107, 151], "uv_size": [1, 66]}, - "up": {"uv": [204, 59], "uv_size": [13, 1]}, - "down": {"uv": [166, 205], "uv_size": [13, -1]} - } - }, - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, 45], - "uv": { - "north": {"uv": [0, 132], "uv_size": [13, 66]}, - "east": {"uv": [108, 151], "uv_size": [1, 66]}, - "south": {"uv": [13, 132], "uv_size": [13, 66]}, - "west": {"uv": [109, 151], "uv_size": [1, 66]}, - "up": {"uv": [204, 184], "uv_size": [13, 1]}, - "down": {"uv": [204, 186], "uv_size": [13, -1]} - } - }, - { - "origin": [-33, 33.43589, -0.7], - "size": [66, 13.12822, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [143, 65], "uv_size": [66, 13]}, - "east": {"uv": [91, 180], "uv_size": [1, 13]}, - "south": {"uv": [143, 78], "uv_size": [66, 13]}, - "west": {"uv": [82, 187], "uv_size": [1, 13]}, - "up": {"uv": [143, 156], "uv_size": [66, 1]}, - "down": {"uv": [143, 158], "uv_size": [66, -1]} - } - }, - { - "origin": [-33, 33.43589, -0.7], - "size": [66, 13.12822, 1], - "uv": { - "north": {"uv": [143, 91], "uv_size": [66, 13]}, - "east": {"uv": [126, 191], "uv_size": [1, 13]}, - "south": {"uv": [143, 104], "uv_size": [66, 13]}, - "west": {"uv": [127, 191], "uv_size": [1, 13]}, - "up": {"uv": [143, 158], "uv_size": [66, 1]}, - "down": {"uv": [143, 160], "uv_size": [66, -1]} - } - }, - { - "origin": [-33, 33.43589, -0.7], - "size": [66, 13.12822, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [143, 117], "uv_size": [66, 13]}, - "east": {"uv": [126, 204], "uv_size": [1, 13]}, - "south": {"uv": [143, 130], "uv_size": [66, 13]}, - "west": {"uv": [127, 204], "uv_size": [1, 13]}, - "up": {"uv": [159, 143], "uv_size": [66, 1]}, - "down": {"uv": [159, 145], "uv_size": [66, -1]} - } - } - ] - }, - { - "name": "portal2", - "parent": "bone9", - "pivot": [0, 40, -0.7], - "cubes": [ - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, -45], - "uv": { - "north": {"uv": [0, 0], "uv_size": [13, 66]}, - "east": {"uv": [128, 145], "uv_size": [1, 66]}, - "south": {"uv": [13, 0], "uv_size": [13, 66]}, - "west": {"uv": [129, 145], "uv_size": [1, 66]}, - "up": {"uv": [78, 65], "uv_size": [13, 1]}, - "down": {"uv": [91, 66], "uv_size": [13, -1]} - } - }, - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [26, 0], "uv_size": [13, 66]}, - "east": {"uv": [92, 151], "uv_size": [1, 66]}, - "south": {"uv": [39, 0], "uv_size": [13, 66]}, - "west": {"uv": [93, 151], "uv_size": [1, 66]}, - "up": {"uv": [104, 65], "uv_size": [13, 1]}, - "down": {"uv": [159, 154], "uv_size": [13, -1]} - } - }, - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "uv": { - "north": {"uv": [52, 0], "uv_size": [13, 66]}, - "east": {"uv": [94, 151], "uv_size": [1, 66]}, - "south": {"uv": [65, 0], "uv_size": [13, 66]}, - "west": {"uv": [95, 151], "uv_size": [1, 66]}, - "up": {"uv": [159, 154], "uv_size": [13, 1]}, - "down": {"uv": [172, 154], "uv_size": [13, -1]} - } - }, - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [0, 66], "uv_size": [13, 66]}, - "east": {"uv": [96, 151], "uv_size": [1, 66]}, - "south": {"uv": [13, 66], "uv_size": [13, 66]}, - "west": {"uv": [97, 151], "uv_size": [1, 66]}, - "up": {"uv": [172, 154], "uv_size": [13, 1]}, - "down": {"uv": [185, 154], "uv_size": [13, -1]} - } - }, - { - "origin": [-6.56411, 7, -0.7], - "size": [13.12822, 66, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, 45], - "uv": { - "north": {"uv": [26, 66], "uv_size": [13, 66]}, - "east": {"uv": [98, 151], "uv_size": [1, 66]}, - "south": {"uv": [39, 66], "uv_size": [13, 66]}, - "west": {"uv": [99, 151], "uv_size": [1, 66]}, - "up": {"uv": [185, 154], "uv_size": [13, 1]}, - "down": {"uv": [191, 59], "uv_size": [13, -1]} - } - }, - { - "origin": [-33, 33.43589, -0.7], - "size": [66, 13.12822, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [52, 66], "uv_size": [66, 13]}, - "east": {"uv": [126, 165], "uv_size": [1, 13]}, - "south": {"uv": [78, 0], "uv_size": [66, 13]}, - "west": {"uv": [127, 165], "uv_size": [1, 13]}, - "up": {"uv": [144, 60], "uv_size": [66, 1]}, - "down": {"uv": [144, 62], "uv_size": [66, -1]} - } - }, - { - "origin": [-33, 33.43589, -0.7], - "size": [66, 13.12822, 1], - "uv": { - "north": {"uv": [78, 13], "uv_size": [66, 13]}, - "east": {"uv": [90, 167], "uv_size": [1, 13]}, - "south": {"uv": [78, 26], "uv_size": [66, 13]}, - "west": {"uv": [91, 167], "uv_size": [1, 13]}, - "up": {"uv": [144, 62], "uv_size": [66, 1]}, - "down": {"uv": [144, 64], "uv_size": [66, -1]} - } - }, - { - "origin": [-33, 33.43589, -0.7], - "size": [66, 13.12822, 1], - "pivot": [0, 40, 1.3], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [78, 39], "uv_size": [66, 13]}, - "east": {"uv": [126, 178], "uv_size": [1, 13]}, - "south": {"uv": [78, 52], "uv_size": [66, 13]}, - "west": {"uv": [127, 178], "uv_size": [1, 13]}, - "up": {"uv": [144, 64], "uv_size": [66, 1]}, - "down": {"uv": [143, 156], "uv_size": [66, -1]} - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/geo/entity/marauder.geo.json b/common/src/main/resources/assets/azurelib/geo/entity/marauder.geo.json deleted file mode 100644 index 2cd28b0b0..000000000 --- a/common/src/main/resources/assets/azurelib/geo/entity/marauder.geo.json +++ /dev/null @@ -1,4168 +0,0 @@ -{ - "format_version": "1.21.0", - "minecraft:geometry": [ - { - "description": { - "identifier": "geometry.unknown", - "texture_width": 512, - "texture_height": 128, - "visible_bounds_width": 14, - "visible_bounds_height": 8, - "visible_bounds_offset": [0, 2, 0] - }, - "bones": [ - { - "name": "dreadnought", - "pivot": [0, 0, 0] - }, - { - "name": "body2", - "parent": "dreadnought", - "pivot": [0, 21, 0] - }, - { - "name": "torso", - "parent": "body2", - "pivot": [0, 21, 0], - "cubes": [ - { - "origin": [-4, 19, -2], - "size": [8, 7, 4], - "uv": { - "north": {"uv": [411, 18], "uv_size": [8, 7]}, - "east": {"uv": [384, 55], "uv_size": [4, 7]}, - "south": {"uv": [411, 25], "uv_size": [8, 7]}, - "west": {"uv": [423, 55], "uv_size": [4, 7]}, - "up": {"uv": [438, 27], "uv_size": [8, 4]}, - "down": {"uv": [439, 49], "uv_size": [8, -4]} - } - }, - { - "origin": [-4, 22.25, -2], - "size": [8, 2, 4], - "inflate": 0.25, - "uv": { - "north": {"uv": [451, 33], "uv_size": [8, 2]}, - "east": {"uv": [456, 45], "uv_size": [4, 2]}, - "south": {"uv": [451, 43], "uv_size": [8, 2]}, - "west": {"uv": [456, 47], "uv_size": [4, 2]} - } - } - ] - }, - { - "name": "upper_torso", - "parent": "torso", - "pivot": [0, 25, 0], - "cubes": [ - { - "origin": [-5.5, 25, -2.5], - "size": [11, 9, 5], - "uv": { - "north": {"uv": [403, 0], "uv_size": [11, 9]}, - "east": {"uv": [423, 35], "uv_size": [5, 9]}, - "south": {"uv": [403, 9], "uv_size": [11, 9]}, - "west": {"uv": [425, 0], "uv_size": [5, 9]}, - "up": {"uv": [414, 0], "uv_size": [11, 5]}, - "down": {"uv": [414, 10], "uv_size": [11, -5]} - } - }, - { - "origin": [-2, 34, -2], - "size": [4, 2, 4], - "uv": { - "north": {"uv": [430, 7], "uv_size": [4, 2]}, - "east": {"uv": [446, 29], "uv_size": [4, 2]}, - "south": {"uv": [457, 25], "uv_size": [4, 2]}, - "west": {"uv": [457, 27], "uv_size": [4, 2]}, - "up": {"uv": [449, 9], "uv_size": [4, 4]}, - "down": {"uv": [395, 69], "uv_size": [4, -4]} - } - }, - { - "origin": [0, 27, -2.75], - "size": [6, 7, 5.5], - "inflate": 0.01, - "pivot": [3, 30, 0], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [428, 9], "uv_size": [6, 7]}, - "east": {"uv": [430, 0], "uv_size": [6, 7]}, - "south": {"uv": [427, 46], "uv_size": [6, 7]}, - "west": {"uv": [431, 16], "uv_size": [6, 7]}, - "up": {"uv": [433, 41], "uv_size": [6, 6]}, - "down": {"uv": [433, 53], "uv_size": [6, -6]} - } - }, - { - "origin": [1.75, 29, -2.85], - "size": [3, 3, 0], - "inflate": 0.01, - "uv": { - "north": {"uv": [387, 79], "uv_size": [-3, 3]}, - "east": {"uv": [430, 0], "uv_size": [6, 7]}, - "south": {"uv": [384, 79], "uv_size": [3, 3]}, - "west": {"uv": [431, 16], "uv_size": [6, 7]}, - "up": {"uv": [433, 41], "uv_size": [6, 6]}, - "down": {"uv": [433, 53], "uv_size": [6, -6]} - } - }, - { - "origin": [-6.77164, 29.14805, -2.75], - "size": [7, 2, 0], - "pivot": [0.22836, 31.14805, -2.75], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [438, 29], "uv_size": [-7, 2]}, - "east": {"uv": [384, 0], "uv_size": [0, 2]}, - "south": {"uv": [431, 29], "uv_size": [7, 2]}, - "west": {"uv": [384, 0], "uv_size": [0, 2]}, - "up": {"uv": [384, 0], "uv_size": [7, 0]}, - "down": {"uv": [384, 0], "uv_size": [7, 0]} - } - }, - { - "origin": [-3.39104, 29.23463, -3], - "size": [2, 4, 6], - "pivot": [-4.39104, 29.23463, -2.75], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [393, 72], "uv_size": [2, 4]}, - "east": {"uv": [395, 61], "uv_size": [6, 4]}, - "south": {"uv": [410, 72], "uv_size": [2, 4]}, - "west": {"uv": [445, 31], "uv_size": [6, 4]}, - "up": {"uv": [436, 0], "uv_size": [2, 6]}, - "down": {"uv": [410, 68], "uv_size": [2, -6]} - } - }, - { - "origin": [-3.89104, 31.73463, -2.75], - "size": [3, 3, 5.5], - "pivot": [-4.39104, 29.23463, -2.75], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [455, 49], "uv_size": [3, 3]}, - "east": {"uv": [447, 54], "uv_size": [6, 3]}, - "south": {"uv": [456, 13], "uv_size": [3, 3]}, - "west": {"uv": [448, 0], "uv_size": [6, 3]}, - "up": {"uv": [396, 50], "uv_size": [3, 6]}, - "down": {"uv": [449, 9], "uv_size": [3, -6]} - } - } - ] - }, - { - "name": "h_head_furious", - "parent": "upper_torso", - "pivot": [0, 35, 0], - "cubes": [ - { - "origin": [-3.5, 34.5, -4], - "size": [7, 4, 4], - "inflate": 0.25, - "pivot": [0, 37.5, -2], - "rotation": [22.5, 0, 0], - "uv": { - "north": {"uv": [442, 5], "uv_size": [7, 4]}, - "east": {"uv": [449, 13], "uv_size": [4, 4]}, - "south": {"uv": [442, 9], "uv_size": [7, 4]}, - "west": {"uv": [449, 17], "uv_size": [4, 4]}, - "up": {"uv": [442, 13], "uv_size": [7, 4]}, - "down": {"uv": [442, 21], "uv_size": [7, -4]} - } - }, - { - "origin": [-3.5, 35, -3.5], - "size": [7, 7, 7], - "uv": { - "north": {"uv": [414, 10], "uv_size": [7, 7]}, - "east": {"uv": [389, 34], "uv_size": [7, 7]}, - "south": {"uv": [396, 34], "uv_size": [7, 7]}, - "west": {"uv": [419, 17], "uv_size": [7, 7]}, - "up": {"uv": [419, 24], "uv_size": [7, 7]}, - "down": {"uv": [421, 17], "uv_size": [7, -7]} - } - }, - { - "origin": [-0.1, 39.75, -5.25], - "size": [0.1, 2, 2], - "pivot": [0, 40, -3.5], - "rotation": [-45, 0, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [0, 2]}, - "east": {"uv": [446, 72], "uv_size": [-2, 2]}, - "south": {"uv": [384, 0], "uv_size": [0, 2]}, - "west": {"uv": [444, 72], "uv_size": [2, 2]}, - "up": {"uv": [384, 0], "uv_size": [0, 2]}, - "down": {"uv": [384, 2], "uv_size": [0, -2]} - } - }, - { - "origin": [-0.1, 42, -2.5], - "size": [0.1, 2, 2], - "pivot": [0, 42, -2.5], - "rotation": [-45, 0, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [0, 2]}, - "east": {"uv": [448, 72], "uv_size": [-2, 2]}, - "south": {"uv": [384, 0], "uv_size": [0, 2]}, - "west": {"uv": [446, 72], "uv_size": [2, 2]}, - "up": {"uv": [384, 0], "uv_size": [0, 2]}, - "down": {"uv": [384, 2], "uv_size": [0, -2]} - } - }, - { - "origin": [3.5, 38.5, -7.5], - "size": [3, 0.1, 11], - "pivot": [3.5, 38.501, 0.5], - "rotation": [22.5, 0, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [3, 0]}, - "east": {"uv": [384, 0], "uv_size": [9, 0]}, - "south": {"uv": [384, 0], "uv_size": [3, 0]}, - "west": {"uv": [384, 0], "uv_size": [9, 0]}, - "up": {"uv": [509, 0], "uv_size": [3, 11]}, - "down": {"uv": [509, 11], "uv_size": [3, -11]} - } - }, - { - "origin": [-6.5, 38.5, -7.5], - "size": [3, 0.1, 11], - "pivot": [-3.5, 38.501, 0.5], - "rotation": [22.5, 0, 0], - "uv": { - "north": {"uv": [387, 0], "uv_size": [-3, 0]}, - "east": {"uv": [393, 0], "uv_size": [-9, 0]}, - "south": {"uv": [387, 0], "uv_size": [-3, 0]}, - "west": {"uv": [393, 0], "uv_size": [-9, 0]}, - "up": {"uv": [512, 0], "uv_size": [-3, 11]}, - "down": {"uv": [512, 11], "uv_size": [-3, -11]} - } - }, - { - "origin": [0, 34.25, -5.32843], - "size": [4, 4, 3], - "pivot": [0, 38.25, -5.32843], - "rotation": [0, -22.5, 0], - "uv": { - "north": {"uv": [423, 65], "uv_size": [4, 4]}, - "east": {"uv": [453, 25], "uv_size": [3, 4]}, - "south": {"uv": [449, 57], "uv_size": [4, 4]}, - "west": {"uv": [451, 69], "uv_size": [3, 4]}, - "up": {"uv": [454, 0], "uv_size": [4, 3]}, - "down": {"uv": [454, 38], "uv_size": [4, -3]} - } - }, - { - "origin": [-4, 34.25, -5.32843], - "size": [4, 4, 3], - "pivot": [0, 38.25, -5.32843], - "rotation": [0, 22.5, 0], - "uv": { - "north": {"uv": [427, 65], "uv_size": [-4, 4]}, - "east": {"uv": [454, 69], "uv_size": [-3, 4]}, - "south": {"uv": [453, 57], "uv_size": [-4, 4]}, - "west": {"uv": [456, 25], "uv_size": [-3, 4]}, - "up": {"uv": [458, 0], "uv_size": [-4, 3]}, - "down": {"uv": [458, 38], "uv_size": [-4, -3]} - } - } - ] - }, - { - "name": "eyes_attack", - "parent": "h_head_furious", - "pivot": [-2.9, 38.25, -3.6], - "cubes": [ - { - "origin": [-3.4, 38.25, -3.6], - "size": [2.5, 2, 0.1], - "pivot": [-2.9, 38.25, -3.6], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [454, 12], "uv_size": [-1, 2]}, - "east": {"uv": [426, 17], "uv_size": [-7, 7]}, - "south": {"uv": [454, 12], "uv_size": [-1, 2]}, - "west": {"uv": [396, 34], "uv_size": [-7, 7]}, - "up": {"uv": [426, 24], "uv_size": [-7, 7]}, - "down": {"uv": [428, 17], "uv_size": [-7, -7]} - } - }, - { - "origin": [0.9, 38.25, -3.6], - "size": [2.5, 2, 0.1], - "pivot": [2.9, 38.25, -3.6], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [453, 12], "uv_size": [1, 2]}, - "east": {"uv": [389, 34], "uv_size": [7, 7]}, - "south": {"uv": [453, 12], "uv_size": [1, 2]}, - "west": {"uv": [419, 17], "uv_size": [7, 7]}, - "up": {"uv": [419, 24], "uv_size": [7, 7]}, - "down": {"uv": [421, 17], "uv_size": [7, -7]} - } - }, - { - "origin": [1.4, 38.25, -3.8], - "size": [1, 1, 0.1], - "uv": { - "north": {"uv": [451, 126], "uv_size": [1, 2]}, - "east": {"uv": [389, 34], "uv_size": [7, 7]}, - "south": {"uv": [393, 15], "uv_size": [1, 2]}, - "west": {"uv": [419, 17], "uv_size": [7, 7]}, - "up": {"uv": [419, 24], "uv_size": [7, 7]}, - "down": {"uv": [421, 17], "uv_size": [7, -7]} - } - }, - { - "origin": [-2.4, 38.25, -3.8], - "size": [1, 1, 0.1], - "uv": { - "north": {"uv": [452, 126], "uv_size": [-1, 2]}, - "east": {"uv": [426, 17], "uv_size": [-7, 7]}, - "south": {"uv": [394, 15], "uv_size": [-1, 2]}, - "west": {"uv": [396, 34], "uv_size": [-7, 7]}, - "up": {"uv": [426, 24], "uv_size": [-7, 7]}, - "down": {"uv": [428, 17], "uv_size": [-7, -7]} - } - } - ] - }, - { - "name": "eyes_normal", - "parent": "h_head_furious", - "pivot": [-2.9, 38.25, -3.6], - "cubes": [ - { - "origin": [-3.4, 38.25, -3.6], - "size": [2.5, 2, 0.1], - "pivot": [-2.9, 38.25, -3.6], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [456, 13], "uv_size": [-2, 2]}, - "east": {"uv": [426, 17], "uv_size": [-7, 7]}, - "south": {"uv": [456, 13], "uv_size": [-2, 2]}, - "west": {"uv": [396, 34], "uv_size": [-7, 7]}, - "up": {"uv": [426, 24], "uv_size": [-7, 7]}, - "down": {"uv": [428, 17], "uv_size": [-7, -7]} - } - }, - { - "origin": [0.9, 38.25, -3.6], - "size": [2.5, 2, 0.1], - "pivot": [2.9, 38.25, -3.6], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [456, 13], "uv_size": [-2, 2]}, - "east": {"uv": [389, 34], "uv_size": [7, 7]}, - "south": {"uv": [456, 13], "uv_size": [-2, 2]}, - "west": {"uv": [419, 17], "uv_size": [7, 7]}, - "up": {"uv": [419, 24], "uv_size": [7, 7]}, - "down": {"uv": [421, 17], "uv_size": [7, -7]} - } - }, - { - "origin": [-2.4, 38.25, -3.8], - "size": [1, 1, 0.1], - "uv": { - "north": {"uv": [454, 12], "uv_size": [-1, 2]}, - "east": {"uv": [454, 12], "uv_size": [-1, 2]}, - "south": {"uv": [454, 12], "uv_size": [-1, 2]}, - "west": {"uv": [454, 12], "uv_size": [-1, 2]}, - "up": {"uv": [453, 14], "uv_size": [1, -2]}, - "down": {"uv": [428, 17], "uv_size": [-7, -7]} - } - }, - { - "origin": [1.4, 38.25, -3.8], - "size": [1, 1, 0.1], - "uv": { - "north": {"uv": [454, 12], "uv_size": [-1, 2]}, - "east": {"uv": [454, 12], "uv_size": [-1, 2]}, - "south": {"uv": [454, 12], "uv_size": [-1, 2]}, - "west": {"uv": [454, 12], "uv_size": [-1, 2]}, - "up": {"uv": [453, 14], "uv_size": [1, -2]}, - "down": {"uv": [421, 17], "uv_size": [7, -7]} - } - } - ] - }, - { - "name": "h_left_horn_furious", - "parent": "h_head_furious", - "pivot": [0, 40.5, 0], - "rotation": [60, -15, -15], - "cubes": [ - { - "origin": [1.5, 41, -3], - "size": [3, 5, 3], - "pivot": [2.5, 42.5, -1.5], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [427, 65], "uv_size": [3, 5]}, - "east": {"uv": [388, 68], "uv_size": [3, 5]}, - "south": {"uv": [453, 7], "uv_size": [3, 5]}, - "west": {"uv": [395, 69], "uv_size": [3, 5]}, - "up": {"uv": [398, 72], "uv_size": [3, 3]}, - "down": {"uv": [401, 75], "uv_size": [3, -3]} - } - }, - { - "origin": [3.18715, 44.96821, -2.5], - "size": [2, 5, 2], - "pivot": [5.68715, 44.96821, -3], - "rotation": [-22.5, 0, 0], - "uv": { - "north": {"uv": [455, 38], "uv_size": [2, 5]}, - "east": {"uv": [440, 71], "uv_size": [2, 5]}, - "south": {"uv": [442, 71], "uv_size": [2, 5]}, - "west": {"uv": [384, 72], "uv_size": [2, 5]}, - "up": {"uv": [456, 64], "uv_size": [2, 2]}, - "down": {"uv": [456, 68], "uv_size": [2, -2]} - } - }, - { - "origin": [4.08715, 47.39627, -0.62464], - "size": [0.1, 2, 5], - "pivot": [4.18715, 49.39627, -0.62464], - "rotation": [22.5, 0, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [0, 5]}, - "east": {"uv": [394, 79], "uv_size": [-5, -2], "uv_rotation": 90}, - "south": {"uv": [384, 0], "uv_size": [0, 5]}, - "west": {"uv": [389, 79], "uv_size": [5, -2], "uv_rotation": 90}, - "up": {"uv": [384, 0], "uv_size": [0, 2]}, - "down": {"uv": [384, 2], "uv_size": [0, -2]} - } - } - ] - }, - { - "name": "h_right_horn_furious", - "parent": "h_head_furious", - "pivot": [0, 40.5, 0], - "rotation": [60, 15, 15], - "cubes": [ - { - "origin": [-4.5, 41, -3], - "size": [3, 5, 3], - "pivot": [-2.5, 42.5, -1.5], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [430, 65], "uv_size": [-3, 5]}, - "east": {"uv": [398, 69], "uv_size": [-3, 5]}, - "south": {"uv": [456, 7], "uv_size": [-3, 5]}, - "west": {"uv": [391, 68], "uv_size": [-3, 5]}, - "up": {"uv": [401, 72], "uv_size": [-3, 3]}, - "down": {"uv": [404, 75], "uv_size": [-3, -3]} - } - }, - { - "origin": [-5.18715, 44.96821, -2.5], - "size": [2, 5, 2], - "pivot": [-5.68715, 44.96821, -3], - "rotation": [-22.5, 0, 0], - "uv": { - "north": {"uv": [391, 123], "uv_size": [2, 5]}, - "east": {"uv": [393, 123], "uv_size": [2, 5]}, - "south": {"uv": [389, 123], "uv_size": [2, 5]}, - "west": {"uv": [395, 123], "uv_size": [2, 5]}, - "up": {"uv": [391, 123], "uv_size": [-2, -2]}, - "down": {"uv": [393, 123], "uv_size": [-2, -2]} - } - }, - { - "origin": [-4.28715, 47.39627, -0.62464], - "size": [0.1, 2, 5], - "pivot": [-4.18715, 49.39627, -0.62464], - "rotation": [22.5, 0, 0], - "uv": { - "north": {"uv": [395, 85], "uv_size": [-5, -2], "uv_rotation": 90}, - "east": {"uv": [394, 79], "uv_size": [-5, -2], "uv_rotation": 90}, - "south": {"uv": [395, 85], "uv_size": [-5, -2], "uv_rotation": 90}, - "west": {"uv": [389, 79], "uv_size": [5, -2], "uv_rotation": 90}, - "up": {"uv": [390, 83], "uv_size": [5, 2], "uv_rotation": 90}, - "down": {"uv": [390, 83], "uv_size": [5, 2], "uv_rotation": 90} - } - } - ] - }, - { - "name": "left_arm", - "parent": "upper_torso", - "pivot": [6, 32.5, 0], - "rotation": [0, 0, -22.5], - "cubes": [ - { - "origin": [4, 26, -2], - "size": [5, 9, 4], - "uv": { - "north": {"uv": [389, 41], "uv_size": [5, 9]}, - "east": {"uv": [388, 50], "uv_size": [4, 9]}, - "south": {"uv": [394, 41], "uv_size": [5, 9]}, - "west": {"uv": [434, 7], "uv_size": [4, 9]}, - "up": {"uv": [446, 41], "uv_size": [5, 4]}, - "down": {"uv": [436, 66], "uv_size": [5, -4]} - } - }, - { - "origin": [7, 30, -2], - "size": [4, 0.1, 4], - "pivot": [9, 29, 0], - "rotation": [0, -45, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [4, 0]}, - "east": {"uv": [384, 0], "uv_size": [4, 0]}, - "south": {"uv": [384, 0], "uv_size": [4, 0]}, - "west": {"uv": [384, 0], "uv_size": [4, 0]}, - "up": {"uv": [449, 61], "uv_size": [4, 4]}, - "down": {"uv": [449, 65], "uv_size": [4, -4]} - } - }, - { - "origin": [6.75, 29, -1.75], - "size": [4, 0.1, 4], - "pivot": [9, 29, 0], - "rotation": [0, -45, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [4, 0]}, - "east": {"uv": [384, 0], "uv_size": [4, 0]}, - "south": {"uv": [384, 0], "uv_size": [4, 0]}, - "west": {"uv": [384, 0], "uv_size": [4, 0]}, - "up": {"uv": [449, 61], "uv_size": [4, 4]}, - "down": {"uv": [449, 65], "uv_size": [4, -4]} - } - }, - { - "origin": [6.5, 28, -1.5], - "size": [4, 0.1, 4], - "pivot": [9, 29, 0], - "rotation": [0, -45, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [4, 0]}, - "east": {"uv": [384, 0], "uv_size": [4, 0]}, - "south": {"uv": [384, 0], "uv_size": [4, 0]}, - "west": {"uv": [384, 0], "uv_size": [4, 0]}, - "up": {"uv": [449, 61], "uv_size": [4, 4]}, - "down": {"uv": [449, 65], "uv_size": [4, -4]} - } - }, - { - "origin": [6, 31, -3], - "size": [4, 5, 6], - "uv": { - "north": {"uv": [441, 62], "uv_size": [4, 5]}, - "east": {"uv": [396, 56], "uv_size": [6, 5]}, - "south": {"uv": [401, 63], "uv_size": [4, 5]}, - "west": {"uv": [406, 57], "uv_size": [6, 5]}, - "up": {"uv": [445, 57], "uv_size": [4, 6]}, - "down": {"uv": [384, 68], "uv_size": [4, -6]} - } - }, - { - "origin": [10, 31, -2.5], - "size": [3, 0.1, 5], - "pivot": [10, 31, 0], - "rotation": [0, 0, -45], - "uv": { - "north": {"uv": [384, 0], "uv_size": [3, 0]}, - "east": {"uv": [384, 0], "uv_size": [5, 0]}, - "south": {"uv": [384, 0], "uv_size": [3, 0]}, - "west": {"uv": [384, 0], "uv_size": [5, 0]}, - "up": {"uv": [423, 69], "uv_size": [3, 5]}, - "down": {"uv": [423, 74], "uv_size": [3, -5]} - } - }, - { - "origin": [8.9, 33, -3.5], - "size": [0.1, 6, 7], - "pivot": [9, 36, 0], - "rotation": [0, 0, 45], - "uv": { - "north": {"uv": [384, 0], "uv_size": [0, 6]}, - "east": {"uv": [438, 23], "uv_size": [-7, 6]}, - "south": {"uv": [384, 0], "uv_size": [0, 6]}, - "west": {"uv": [431, 23], "uv_size": [7, 6]}, - "up": {"uv": [384, 0], "uv_size": [0, 7]}, - "down": {"uv": [384, 7], "uv_size": [0, -7]} - } - } - ] - }, - { - "name": "left_elbow", - "parent": "left_arm", - "pivot": [6.5, 26, 1], - "rotation": [-22.5, 0, 0], - "cubes": [ - { - "origin": [4.5, 17, -2], - "size": [4, 9, 4], - "uv": { - "north": {"uv": [392, 50], "uv_size": [4, 9]}, - "east": {"uv": [413, 51], "uv_size": [4, 9]}, - "south": {"uv": [417, 51], "uv_size": [4, 9]}, - "west": {"uv": [427, 53], "uv_size": [4, 9]}, - "up": {"uv": [451, 49], "uv_size": [4, 4]}, - "down": {"uv": [440, 71], "uv_size": [4, -4]} - } - }, - { - "origin": [5.5, 24, 0.5], - "size": [2, 3, 2], - "uv": { - "north": {"uv": [476, 2], "uv_size": [-2, 3]}, - "east": {"uv": [474, 5], "uv_size": [-2, 3]}, - "south": {"uv": [476, 5], "uv_size": [-2, 3]}, - "west": {"uv": [474, 2], "uv_size": [-2, 3]}, - "up": {"uv": [478, 4], "uv_size": [2, -2]}, - "down": {"uv": [476, 4], "uv_size": [2, -2]} - } - } - ] - }, - { - "name": "hand_boomstick", - "parent": "left_elbow", - "pivot": [6.5, 20, -2], - "cubes": [ - { - "origin": [4.5, 5, -5], - "size": [4, 15, 3], - "uv": { - "north": {"uv": [403, 18], "uv_size": [4, 15]}, - "east": {"uv": [399, 41], "uv_size": [3, 15]}, - "south": {"uv": [407, 18], "uv_size": [4, 15]}, - "west": {"uv": [410, 42], "uv_size": [3, 15]}, - "up": {"uv": [426, 70], "uv_size": [4, 3]}, - "down": {"uv": [436, 73], "uv_size": [4, -3]} - } - }, - { - "origin": [6.75, 4, -5.25], - "size": [2, 13, 2], - "uv": { - "north": {"uv": [412, 60], "uv_size": [2, 13]}, - "east": {"uv": [414, 60], "uv_size": [2, 13]}, - "south": {"uv": [416, 60], "uv_size": [2, 13]}, - "west": {"uv": [418, 60], "uv_size": [2, 13]}, - "up": {"uv": [388, 73], "uv_size": [2, 2]}, - "down": {"uv": [412, 75], "uv_size": [2, -2]} - } - }, - { - "origin": [4.25, 4, -5.25], - "size": [2, 13, 2], - "uv": { - "north": {"uv": [414, 60], "uv_size": [-2, 13]}, - "east": {"uv": [420, 60], "uv_size": [-2, 13]}, - "south": {"uv": [418, 60], "uv_size": [-2, 13]}, - "west": {"uv": [416, 60], "uv_size": [-2, 13]}, - "up": {"uv": [390, 73], "uv_size": [-2, 2]}, - "down": {"uv": [414, 75], "uv_size": [-2, -2]} - } - }, - { - "origin": [5, 17.5, -4], - "size": [3, 3, 7], - "pivot": [6.5, 19, -3], - "rotation": [22.5, 0, 0], - "uv": { - "north": {"uv": [456, 19], "uv_size": [3, 3]}, - "east": {"uv": [446, 26], "uv_size": [7, 3]}, - "south": {"uv": [404, 72], "uv_size": [3, 3]}, - "west": {"uv": [423, 62], "uv_size": [7, 3]}, - "up": {"uv": [430, 62], "uv_size": [3, 7]}, - "down": {"uv": [433, 69], "uv_size": [3, -7]} - } - } - ] - }, - { - "name": "right_arm", - "parent": "upper_torso", - "pivot": [-6, 32.5, 0], - "rotation": [0, 0, 22.5], - "cubes": [ - { - "origin": [-9, 26, -2], - "size": [5, 9, 4], - "uv": { - "north": {"uv": [426, 17], "uv_size": [5, 9]}, - "east": {"uv": [431, 53], "uv_size": [4, 9]}, - "south": {"uv": [413, 42], "uv_size": [5, 9]}, - "west": {"uv": [435, 53], "uv_size": [4, 9]}, - "up": {"uv": [447, 21], "uv_size": [5, 4]}, - "down": {"uv": [447, 49], "uv_size": [5, -4]} - } - }, - { - "origin": [-11, 28, -2], - "size": [4, 0.1, 4], - "pivot": [-9, 27, 0], - "rotation": [0, 45, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [4, 0]}, - "east": {"uv": [384, 0], "uv_size": [4, 0]}, - "south": {"uv": [384, 0], "uv_size": [4, 0]}, - "west": {"uv": [384, 0], "uv_size": [4, 0]}, - "up": {"uv": [452, 3], "uv_size": [4, 4]}, - "down": {"uv": [452, 7], "uv_size": [4, -4]} - } - }, - { - "origin": [-10, 29, -3], - "size": [4, 5, 6], - "uv": { - "north": {"uv": [445, 62], "uv_size": [-4, 5]}, - "east": {"uv": [412, 57], "uv_size": [-6, 5]}, - "south": {"uv": [405, 63], "uv_size": [-4, 5]}, - "west": {"uv": [402, 56], "uv_size": [-6, 5]}, - "up": {"uv": [449, 57], "uv_size": [-4, 6]}, - "down": {"uv": [388, 68], "uv_size": [-4, -6]} - } - }, - { - "origin": [-12, 29.41421, -2.5], - "size": [3, 0.1, 5], - "pivot": [-10, 30.41421, 0], - "rotation": [0, 0, 45], - "uv": { - "north": {"uv": [387, 0], "uv_size": [-3, 0]}, - "east": {"uv": [389, 0], "uv_size": [-5, 0]}, - "south": {"uv": [387, 0], "uv_size": [-3, 0]}, - "west": {"uv": [389, 0], "uv_size": [-5, 0]}, - "up": {"uv": [426, 69], "uv_size": [-3, 5]}, - "down": {"uv": [426, 74], "uv_size": [-3, -5]} - } - }, - { - "origin": [-10.1, 31.41421, -3.5], - "size": [0.1, 6, 7], - "pivot": [-9, 35.41421, 0], - "rotation": [0, 0, -45], - "uv": { - "north": {"uv": [384, 0], "uv_size": [0, 6]}, - "east": {"uv": [438, 23], "uv_size": [-7, 6]}, - "south": {"uv": [384, 0], "uv_size": [0, 6]}, - "west": {"uv": [431, 23], "uv_size": [7, 6]}, - "up": {"uv": [384, 0], "uv_size": [0, 7]}, - "down": {"uv": [384, 7], "uv_size": [0, -7]} - } - } - ] - }, - { - "name": "right_elbow", - "parent": "right_arm", - "pivot": [-6.5, 26, 1], - "rotation": [-22.5, 0, 0], - "cubes": [ - { - "origin": [-8.5, 17, -2], - "size": [4, 9, 4], - "uv": { - "north": {"uv": [438, 0], "uv_size": [4, 9]}, - "east": {"uv": [438, 9], "uv_size": [4, 9]}, - "south": {"uv": [402, 54], "uv_size": [4, 9]}, - "west": {"uv": [438, 18], "uv_size": [4, 9]}, - "up": {"uv": [399, 68], "uv_size": [4, 4]}, - "down": {"uv": [403, 72], "uv_size": [4, -4]} - } - }, - { - "origin": [-7.5, 24, 0.5], - "size": [2, 3, 2], - "uv": { - "north": {"uv": [474, 2], "uv_size": [2, 3]}, - "east": {"uv": [472, 2], "uv_size": [2, 3]}, - "south": {"uv": [474, 5], "uv_size": [2, 3]}, - "west": {"uv": [472, 5], "uv_size": [2, 3]}, - "up": {"uv": [480, 4], "uv_size": [-2, -2]}, - "down": {"uv": [478, 4], "uv_size": [-2, -2]} - } - } - ] - }, - { - "name": "axe", - "parent": "right_elbow", - "pivot": [-6.5, 19, -1], - "rotation": [45, 45, -15], - "cubes": [ - { - "origin": [-6.6, 10.5, -32], - "size": [0.1, 17, 19], - "uv": { - "north": {"uv": [384, 0], "uv_size": [0, 17]}, - "east": {"uv": [461, 111], "uv_size": [-19, 17]}, - "south": {"uv": [384, 0], "uv_size": [0, 17]}, - "west": {"uv": [442, 111], "uv_size": [19, 17]}, - "up": {"uv": [384, 0], "uv_size": [0, 19]}, - "down": {"uv": [384, 19], "uv_size": [0, -19]} - } - }, - { - "origin": [-7.5, 18, -26], - "size": [2, 2, 32], - "uv": { - "north": {"uv": [414, 73], "uv_size": [2, 2]}, - "east": {"uv": [421, 31], "uv_size": [24, 2]}, - "south": {"uv": [416, 73], "uv_size": [2, 2]}, - "west": {"uv": [421, 33], "uv_size": [24, 2]}, - "up": {"uv": [408, 33], "uv_size": [2, 24]}, - "down": {"uv": [421, 59], "uv_size": [2, -24]} - } - }, - { - "origin": [-7.5, 17.5, -31], - "size": [2, 3, 3], - "uv": { - "north": {"uv": [399, 65], "uv_size": [2, 3]}, - "east": {"uv": [456, 22], "uv_size": [3, 3]}, - "south": {"uv": [420, 72], "uv_size": [2, 3]}, - "west": {"uv": [407, 72], "uv_size": [3, 3]}, - "up": {"uv": [456, 52], "uv_size": [2, 3]}, - "down": {"uv": [456, 58], "uv_size": [2, -3]} - } - }, - { - "origin": [-6.5, 17.5, -32.5], - "size": [0, 4, 4], - "pivot": [-6.5, 19, -31], - "rotation": [-45, 0, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [0, 4]}, - "east": {"uv": [407, 68], "uv_size": [4, 4]}, - "south": {"uv": [384, 0], "uv_size": [0, 4]}, - "west": {"uv": [411, 68], "uv_size": [-4, 4]}, - "up": {"uv": [384, 0], "uv_size": [0, 4]}, - "down": {"uv": [384, 4], "uv_size": [0, -4]} - } - } - ] - }, - { - "name": "inactive_axe", - "parent": "right_elbow", - "pivot": [-6.5, 19, -1], - "rotation": [45, 45, -15], - "cubes": [ - { - "origin": [-7.5, 18, -26], - "size": [2, 2, 32], - "uv": { - "north": {"uv": [414, 73], "uv_size": [2, 2]}, - "east": {"uv": [421, 31], "uv_size": [24, 2]}, - "south": {"uv": [416, 73], "uv_size": [2, 2]}, - "west": {"uv": [421, 33], "uv_size": [24, 2]}, - "up": {"uv": [408, 33], "uv_size": [2, 24]}, - "down": {"uv": [421, 59], "uv_size": [2, -24]} - } - }, - { - "origin": [-7.5, 17.5, -29], - "size": [2, 3, 3], - "uv": { - "north": {"uv": [404, 75], "uv_size": [2, 3]}, - "east": {"uv": [459, 22], "uv_size": [3, 3]}, - "south": {"uv": [420, 75], "uv_size": [2, 3]}, - "west": {"uv": [407, 75], "uv_size": [3, 3]}, - "up": {"uv": [458, 52], "uv_size": [2, 3]}, - "down": {"uv": [458, 58], "uv_size": [2, -3]} - } - }, - { - "origin": [-6.5, 17.5, -30.5], - "size": [0, 4, 4], - "pivot": [-6.5, 19, -29], - "rotation": [-45, 0, 0], - "uv": { - "north": {"uv": [384, 0], "uv_size": [0, 4]}, - "east": {"uv": [407, 68], "uv_size": [4, 4]}, - "south": {"uv": [384, 0], "uv_size": [0, 4]}, - "west": {"uv": [411, 68], "uv_size": [-4, 4]}, - "up": {"uv": [384, 0], "uv_size": [0, 4]}, - "down": {"uv": [384, 4], "uv_size": [0, -4]} - } - } - ] - }, - { - "name": "b_torso", - "parent": "torso", - "pivot": [0, 50, 0], - "cubes": [ - { - "origin": [-8, 16, -8], - "size": [16, 16, 16], - "uv": { - "north": {"uv": [384, 0], "uv_size": [16, 16]}, - "east": {"uv": [384, 0], "uv_size": [16, 16]}, - "south": {"uv": [384, 0], "uv_size": [16, 16]}, - "west": {"uv": [384, 0], "uv_size": [16, 16]}, - "up": {"uv": [400, 16], "uv_size": [-16, -16]}, - "down": {"uv": [400, 16], "uv_size": [-16, -16]} - } - } - ] - }, - { - "name": "b_head", - "parent": "torso", - "pivot": [0, 66, 0], - "cubes": [ - { - "origin": [-4, 32, -4], - "size": [8, 8, 8], - "uv": { - "north": {"uv": [384, 0], "uv_size": [8, 8]}, - "east": {"uv": [384, 0], "uv_size": [8, 8]}, - "south": {"uv": [384, 0], "uv_size": [8, 8]}, - "west": {"uv": [384, 0], "uv_size": [8, 8]}, - "up": {"uv": [392, 8], "uv_size": [-8, -8]}, - "down": {"uv": [392, 8], "uv_size": [-8, -8]} - } - } - ] - }, - { - "name": "left_leg", - "parent": "body2", - "pivot": [2.5, 21, 0], - "rotation": [0, -10, -5], - "cubes": [ - { - "origin": [0, 11, -2.5], - "size": [5, 10, 5], - "uv": { - "north": {"uv": [411, 32], "uv_size": [5, 10]}, - "east": {"uv": [416, 32], "uv_size": [5, 10]}, - "south": {"uv": [403, 33], "uv_size": [5, 10]}, - "west": {"uv": [384, 34], "uv_size": [5, 10]}, - "up": {"uv": [426, 26], "uv_size": [5, 5]}, - "down": {"uv": [442, 26], "uv_size": [5, -5]} - } - }, - { - "origin": [2, 17, -2.75], - "size": [3, 5, 0.1], - "pivot": [4, 18.5, -2.75], - "rotation": [22.5, 0, 0], - "uv": { - "north": {"uv": [456, 53], "uv_size": [-3, 5]}, - "east": {"uv": [384, 0], "uv_size": [0, 5]}, - "south": {"uv": [453, 53], "uv_size": [3, 5]}, - "west": {"uv": [384, 0], "uv_size": [0, 5]}, - "up": {"uv": [384, 0], "uv_size": [3, 0]}, - "down": {"uv": [384, 0], "uv_size": [3, 0]} - } - }, - { - "origin": [2, 15, -2.75], - "size": [3, 4, 0.1], - "pivot": [4, 15.5, -2.75], - "rotation": [0, 0, 22.5], - "uv": { - "north": {"uv": [457, 68], "uv_size": [-3, 4]}, - "east": {"uv": [384, 0], "uv_size": [0, 4]}, - "south": {"uv": [454, 68], "uv_size": [3, 4]}, - "west": {"uv": [384, 0], "uv_size": [0, 4]}, - "up": {"uv": [384, 0], "uv_size": [3, 0]}, - "down": {"uv": [384, 0], "uv_size": [3, 0]} - } - } - ] - }, - { - "name": "left_knee", - "parent": "left_leg", - "pivot": [2.5, 11, -1.5], - "rotation": [5, 0, 0], - "cubes": [ - { - "origin": [0.5, 0, -2.5], - "size": [4, 11, 4], - "uv": { - "north": {"uv": [402, 43], "uv_size": [4, 11]}, - "east": {"uv": [384, 44], "uv_size": [4, 11]}, - "south": {"uv": [428, 35], "uv_size": [4, 11]}, - "west": {"uv": [423, 44], "uv_size": [4, 11]}, - "up": {"uv": [452, 45], "uv_size": [4, 4]}, - "down": {"uv": [444, 72], "uv_size": [4, -4]} - } - }, - { - "origin": [1, 8, -3.5], - "size": [3, 5, 2], - "uv": { - "north": {"uv": [453, 63], "uv_size": [3, 5]}, - "east": {"uv": [391, 72], "uv_size": [2, 5]}, - "south": {"uv": [448, 69], "uv_size": [3, 5]}, - "west": {"uv": [456, 8], "uv_size": [2, 5]}, - "up": {"uv": [453, 19], "uv_size": [3, 2]}, - "down": {"uv": [456, 60], "uv_size": [3, -2]} - } - } - ] - }, - { - "name": "leg_boomstick", - "parent": "left_leg", - "pivot": [6.5, 21, 0], - "cubes": [ - { - "origin": [5, 19, -0.5], - "size": [3, 3, 7], - "pivot": [6.5, 20.5, 0.5], - "rotation": [22.5, 0, 0], - "uv": { - "north": {"uv": [456, 19], "uv_size": [3, 3]}, - "east": {"uv": [446, 26], "uv_size": [7, 3]}, - "south": {"uv": [404, 72], "uv_size": [3, 3]}, - "west": {"uv": [423, 62], "uv_size": [7, 3]}, - "up": {"uv": [430, 62], "uv_size": [3, 7]}, - "down": {"uv": [433, 69], "uv_size": [3, -7]} - } - }, - { - "origin": [4.5, 6.5, -1.5], - "size": [4, 15, 3], - "uv": { - "north": {"uv": [403, 18], "uv_size": [4, 15]}, - "east": {"uv": [399, 41], "uv_size": [3, 15]}, - "south": {"uv": [407, 18], "uv_size": [4, 15]}, - "west": {"uv": [410, 42], "uv_size": [3, 15]}, - "up": {"uv": [426, 70], "uv_size": [4, 3]}, - "down": {"uv": [436, 73], "uv_size": [4, -3]} - } - }, - { - "origin": [4.25, 5.5, -1.75], - "size": [2, 13, 2], - "uv": { - "north": {"uv": [414, 60], "uv_size": [-2, 13]}, - "east": {"uv": [420, 60], "uv_size": [-2, 13]}, - "south": {"uv": [418, 60], "uv_size": [-2, 13]}, - "west": {"uv": [416, 60], "uv_size": [-2, 13]}, - "up": {"uv": [390, 73], "uv_size": [-2, 2]}, - "down": {"uv": [414, 75], "uv_size": [-2, -2]} - } - }, - { - "origin": [6.75, 5.5, -1.75], - "size": [2, 13, 2], - "uv": { - "north": {"uv": [412, 60], "uv_size": [2, 13]}, - "east": {"uv": [414, 60], "uv_size": [2, 13]}, - "south": {"uv": [416, 60], "uv_size": [2, 13]}, - "west": {"uv": [418, 60], "uv_size": [2, 13]}, - "up": {"uv": [388, 73], "uv_size": [2, 2]}, - "down": {"uv": [412, 75], "uv_size": [2, -2]} - } - } - ] - }, - { - "name": "right_leg", - "parent": "body2", - "pivot": [-2.5, 21, 0], - "rotation": [0, 10, 5], - "cubes": [ - { - "origin": [-5, 11, -2.5], - "size": [5, 10, 5], - "uv": { - "north": {"uv": [416, 32], "uv_size": [-5, 10]}, - "east": {"uv": [389, 34], "uv_size": [-5, 10]}, - "south": {"uv": [408, 33], "uv_size": [-5, 10]}, - "west": {"uv": [421, 32], "uv_size": [-5, 10]}, - "up": {"uv": [431, 26], "uv_size": [-5, 5]}, - "down": {"uv": [447, 26], "uv_size": [-5, -5]} - } - }, - { - "origin": [-5, 17, -2.75], - "size": [3, 5, 0.1], - "pivot": [-4, 18.5, -2.75], - "rotation": [22.5, 0, 0], - "uv": { - "north": {"uv": [453, 53], "uv_size": [3, 5]}, - "east": {"uv": [384, 0], "uv_size": [0, 5]}, - "south": {"uv": [456, 53], "uv_size": [-3, 5]}, - "west": {"uv": [384, 0], "uv_size": [0, 5]}, - "up": {"uv": [387, 0], "uv_size": [-3, 0]}, - "down": {"uv": [387, 0], "uv_size": [-3, 0]} - } - }, - { - "origin": [-5, 15, -2.75], - "size": [3, 4, 0.1], - "pivot": [-4, 15.5, -2.75], - "rotation": [0, 0, -22.5], - "uv": { - "north": {"uv": [454, 68], "uv_size": [3, 4]}, - "east": {"uv": [384, 0], "uv_size": [0, 4]}, - "south": {"uv": [457, 68], "uv_size": [-3, 4]}, - "west": {"uv": [384, 0], "uv_size": [0, 4]}, - "up": {"uv": [387, 0], "uv_size": [-3, 0]}, - "down": {"uv": [387, 0], "uv_size": [-3, 0]} - } - } - ] - }, - { - "name": "right_knee", - "parent": "right_leg", - "pivot": [-2.5, 11, -1.5], - "rotation": [5, 0, 0], - "cubes": [ - { - "origin": [-4.5, 0, -2.5], - "size": [4, 11, 4], - "uv": { - "north": {"uv": [406, 43], "uv_size": [-4, 11]}, - "east": {"uv": [427, 44], "uv_size": [-4, 11]}, - "south": {"uv": [432, 35], "uv_size": [-4, 11]}, - "west": {"uv": [388, 44], "uv_size": [-4, 11]}, - "up": {"uv": [456, 45], "uv_size": [-4, 4]}, - "down": {"uv": [448, 72], "uv_size": [-4, -4]} - } - }, - { - "origin": [-4, 8, -3.5], - "size": [3, 5, 2], - "uv": { - "north": {"uv": [456, 63], "uv_size": [-3, 5]}, - "east": {"uv": [458, 8], "uv_size": [-2, 5]}, - "south": {"uv": [451, 69], "uv_size": [-3, 5]}, - "west": {"uv": [393, 72], "uv_size": [-2, 5]}, - "up": {"uv": [456, 19], "uv_size": [-3, 2]}, - "down": {"uv": [459, 60], "uv_size": [-3, -2]} - } - } - ] - }, - { - "name": "h_slash", - "parent": "body2", - "pivot": [0, 24, -32] - }, - { - "name": "h_slash2", - "parent": "h_slash", - "pivot": [0, 24, -32] - }, - { - "name": "h_slash3", - "parent": "h_slash2", - "pivot": [0, 24, -32] - }, - { - "name": "h_slash4", - "parent": "h_slash3", - "pivot": [0, 24, -32] - }, - { - "name": "h_slash5", - "parent": "h_slash4", - "pivot": [0, 24, -32] - }, - { - "name": "h_slash6", - "parent": "h_slash5", - "pivot": [0, 24, -32] - }, - { - "name": "h_slash7", - "parent": "h_slash6", - "pivot": [0, 24, -32] - }, - { - "name": "h_slash8", - "parent": "h_slash7", - "pivot": [0, 24, -32] - }, - { - "name": "h_slash9", - "parent": "h_slash8", - "pivot": [0, 24, -32] - }, - { - "name": "h_slash10", - "parent": "h_slash9", - "pivot": [0, 24, -32], - "cubes": [ - { - "origin": [-0.1, 10, -48], - "size": [0.1, 32, 32], - "uv": { - "north": {"uv": [384, 0], "uv_size": [0, 32]}, - "east": {"uv": [512, 96], "uv_size": [-32, 32]}, - "south": {"uv": [384, 0], "uv_size": [0, 32]}, - "west": {"uv": [480, 96], "uv_size": [32, 32]}, - "up": {"uv": [384, 32], "uv_size": [0, -32]}, - "down": {"uv": [384, 32], "uv_size": [0, -32]} - } - } - ] - }, - { - "name": "dreadnought_portal", - "pivot": [0, 0, 0] - }, - { - "name": "body", - "parent": "dreadnought_portal", - "pivot": [0, 0, 0] - }, - { - "name": "black", - "parent": "body", - "pivot": [0, 24, 0], - "cubes": [ - { - "origin": [-46, -22, 0], - "size": [92, 92, 0.1], - "uv": { - "north": {"uv": [128, 0], "uv_size": [128, 128]}, - "east": {"uv": [128, 0], "uv_size": [0, 32]}, - "south": {"uv": [256, 0], "uv_size": [-128, 128]}, - "west": {"uv": [128, 0], "uv_size": [0, 32]}, - "up": {"uv": [160, 0], "uv_size": [-32, 0]}, - "down": {"uv": [160, 0], "uv_size": [-32, 0]} - } - } - ] - }, - { - "name": "cluster1", - "parent": "body", - "pivot": [0, 24, 0] - }, - { - "name": "cloud1", - "parent": "cluster1", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud2", - "parent": "cluster1", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud3", - "parent": "cluster1", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud4", - "parent": "cluster1", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster2", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 10] - }, - { - "name": "cloud5", - "parent": "cluster2", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud6", - "parent": "cluster2", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud7", - "parent": "cluster2", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud8", - "parent": "cluster2", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud9", - "parent": "cluster2", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster3", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 22.5] - }, - { - "name": "cloud10", - "parent": "cluster3", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud11", - "parent": "cluster3", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud12", - "parent": "cluster3", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud13", - "parent": "cluster3", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster4", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 32.5] - }, - { - "name": "cloud14", - "parent": "cluster4", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud15", - "parent": "cluster4", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud16", - "parent": "cluster4", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud17", - "parent": "cluster4", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud18", - "parent": "cluster4", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster5", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 45] - }, - { - "name": "cloud19", - "parent": "cluster5", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud20", - "parent": "cluster5", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud21", - "parent": "cluster5", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud22", - "parent": "cluster5", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster6", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 55] - }, - { - "name": "cloud23", - "parent": "cluster6", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud24", - "parent": "cluster6", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud25", - "parent": "cluster6", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud26", - "parent": "cluster6", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud27", - "parent": "cluster6", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster7", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 67.5] - }, - { - "name": "cloud28", - "parent": "cluster7", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud29", - "parent": "cluster7", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud30", - "parent": "cluster7", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud31", - "parent": "cluster7", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster8", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 77.5] - }, - { - "name": "cloud32", - "parent": "cluster8", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud33", - "parent": "cluster8", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud34", - "parent": "cluster8", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud35", - "parent": "cluster8", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud36", - "parent": "cluster8", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster9", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 90] - }, - { - "name": "cloud37", - "parent": "cluster9", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud38", - "parent": "cluster9", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud39", - "parent": "cluster9", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud40", - "parent": "cluster9", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster10", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 100] - }, - { - "name": "cloud41", - "parent": "cluster10", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud42", - "parent": "cluster10", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud43", - "parent": "cluster10", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud44", - "parent": "cluster10", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud45", - "parent": "cluster10", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster11", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 112.5] - }, - { - "name": "cloud46", - "parent": "cluster11", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud47", - "parent": "cluster11", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud48", - "parent": "cluster11", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud49", - "parent": "cluster11", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster12", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 122.5] - }, - { - "name": "cloud50", - "parent": "cluster12", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud51", - "parent": "cluster12", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud52", - "parent": "cluster12", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud53", - "parent": "cluster12", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud54", - "parent": "cluster12", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster13", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 135] - }, - { - "name": "cloud55", - "parent": "cluster13", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud56", - "parent": "cluster13", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud57", - "parent": "cluster13", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud58", - "parent": "cluster13", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster14", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 145] - }, - { - "name": "cloud59", - "parent": "cluster14", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud60", - "parent": "cluster14", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud61", - "parent": "cluster14", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud62", - "parent": "cluster14", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud63", - "parent": "cluster14", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster15", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 157.5] - }, - { - "name": "cloud64", - "parent": "cluster15", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud65", - "parent": "cluster15", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud66", - "parent": "cluster15", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud67", - "parent": "cluster15", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster16", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, 167.5] - }, - { - "name": "cloud68", - "parent": "cluster16", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud69", - "parent": "cluster16", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud70", - "parent": "cluster16", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud71", - "parent": "cluster16", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud72", - "parent": "cluster16", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster17", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -180] - }, - { - "name": "cloud73", - "parent": "cluster17", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud74", - "parent": "cluster17", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud75", - "parent": "cluster17", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud76", - "parent": "cluster17", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster18", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -170] - }, - { - "name": "cloud77", - "parent": "cluster18", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud78", - "parent": "cluster18", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud79", - "parent": "cluster18", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud80", - "parent": "cluster18", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud81", - "parent": "cluster18", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster19", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -157.5] - }, - { - "name": "cloud82", - "parent": "cluster19", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud83", - "parent": "cluster19", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud84", - "parent": "cluster19", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud85", - "parent": "cluster19", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster20", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -147.5] - }, - { - "name": "cloud86", - "parent": "cluster20", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud87", - "parent": "cluster20", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud88", - "parent": "cluster20", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud89", - "parent": "cluster20", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud90", - "parent": "cluster20", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster21", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -135] - }, - { - "name": "cloud91", - "parent": "cluster21", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud92", - "parent": "cluster21", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud93", - "parent": "cluster21", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud94", - "parent": "cluster21", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster22", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -125] - }, - { - "name": "cloud95", - "parent": "cluster22", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud96", - "parent": "cluster22", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud97", - "parent": "cluster22", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud98", - "parent": "cluster22", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud99", - "parent": "cluster22", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster23", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -112.5] - }, - { - "name": "cloud100", - "parent": "cluster23", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud101", - "parent": "cluster23", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud102", - "parent": "cluster23", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud103", - "parent": "cluster23", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster24", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -102.5] - }, - { - "name": "cloud104", - "parent": "cluster24", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud105", - "parent": "cluster24", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud106", - "parent": "cluster24", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud107", - "parent": "cluster24", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud108", - "parent": "cluster24", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster25", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -90] - }, - { - "name": "cloud109", - "parent": "cluster25", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud110", - "parent": "cluster25", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud111", - "parent": "cluster25", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud112", - "parent": "cluster25", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster26", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -80] - }, - { - "name": "cloud113", - "parent": "cluster26", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud114", - "parent": "cluster26", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud115", - "parent": "cluster26", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud116", - "parent": "cluster26", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud117", - "parent": "cluster26", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster27", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -67.5] - }, - { - "name": "cloud118", - "parent": "cluster27", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud119", - "parent": "cluster27", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud120", - "parent": "cluster27", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud121", - "parent": "cluster27", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster28", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -57.5] - }, - { - "name": "cloud122", - "parent": "cluster28", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud123", - "parent": "cluster28", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud124", - "parent": "cluster28", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud125", - "parent": "cluster28", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud126", - "parent": "cluster28", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster29", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -45] - }, - { - "name": "cloud127", - "parent": "cluster29", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud128", - "parent": "cluster29", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud129", - "parent": "cluster29", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud130", - "parent": "cluster29", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster30", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -35] - }, - { - "name": "cloud131", - "parent": "cluster30", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud132", - "parent": "cluster30", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud133", - "parent": "cluster30", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud134", - "parent": "cluster30", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud135", - "parent": "cluster30", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cluster31", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -22.5] - }, - { - "name": "cloud136", - "parent": "cluster31", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [10, 48], "uv_size": [8, 8]}, - "east": {"uv": [34, 52], "uv_size": [8, 8]}, - "south": {"uv": [42, 52], "uv_size": [8, 8]}, - "west": {"uv": [50, 52], "uv_size": [8, 8]}, - "up": {"uv": [54, 42], "uv_size": [8, 8]}, - "down": {"uv": [10, 64], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud137", - "parent": "cluster31", - "pivot": [3, 64, 1], - "cubes": [ - { - "origin": [1, 62, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [56, 10], "uv_size": [8, 8]}, - "east": {"uv": [18, 56], "uv_size": [8, 8]}, - "south": {"uv": [56, 18], "uv_size": [8, 8]}, - "west": {"uv": [26, 56], "uv_size": [8, 8]}, - "up": {"uv": [56, 26], "uv_size": [8, 8]}, - "down": {"uv": [56, 42], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud138", - "parent": "cluster31", - "pivot": [3, 69, 0], - "cubes": [ - { - "origin": [0, 66, -3], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [0, 0], "uv_size": [12, 12]}, - "east": {"uv": [0, 12], "uv_size": [12, 12]}, - "south": {"uv": [12, 0], "uv_size": [12, 12]}, - "west": {"uv": [12, 12], "uv_size": [12, 12]}, - "up": {"uv": [0, 24], "uv_size": [12, 12]}, - "down": {"uv": [24, 12], "uv_size": [12, -12]} - } - } - ] - }, - { - "name": "cloud139", - "parent": "cluster31", - "pivot": [-2, 64, 0], - "cubes": [ - { - "origin": [-4.5, 61.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [36, 12], "uv_size": [10, 10]}, - "east": {"uv": [36, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 36], "uv_size": [10, 10]}, - "west": {"uv": [36, 32], "uv_size": [10, 10]}, - "up": {"uv": [34, 42], "uv_size": [10, 10]}, - "down": {"uv": [44, 52], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cluster32", - "parent": "body", - "pivot": [0, 24, 0], - "rotation": [0, 0, -12.5] - }, - { - "name": "cloud140", - "parent": "cluster32", - "pivot": [0, 67, 0], - "cubes": [ - { - "origin": [-2, 65, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [0, 58], "uv_size": [8, 8]}, - "east": {"uv": [58, 0], "uv_size": [8, 8]}, - "south": {"uv": [58, 50], "uv_size": [8, 8]}, - "west": {"uv": [58, 58], "uv_size": [8, 8]}, - "up": {"uv": [34, 60], "uv_size": [8, 8]}, - "down": {"uv": [42, 68], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud141", - "parent": "cluster32", - "pivot": [4, 65, 1], - "cubes": [ - { - "origin": [2, 63, -1], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [50, 60], "uv_size": [8, 8]}, - "east": {"uv": [62, 42], "uv_size": [8, 8]}, - "south": {"uv": [8, 64], "uv_size": [8, 8]}, - "west": {"uv": [64, 8], "uv_size": [8, 8]}, - "up": {"uv": [16, 64], "uv_size": [8, 8]}, - "down": {"uv": [64, 24], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud142", - "parent": "cluster32", - "pivot": [2, 70, -0.5], - "cubes": [ - { - "origin": [-0.5, 67.5, -2.5], - "size": [5, 5, 5], - "uv": { - "north": {"uv": [46, 12], "uv_size": [10, 10]}, - "east": {"uv": [46, 22], "uv_size": [10, 10]}, - "south": {"uv": [24, 46], "uv_size": [10, 10]}, - "west": {"uv": [46, 32], "uv_size": [10, 10]}, - "up": {"uv": [0, 48], "uv_size": [10, 10]}, - "down": {"uv": [48, 10], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "cloud143", - "parent": "cluster32", - "pivot": [6, 69, 0], - "cubes": [ - { - "origin": [4, 67, -2], - "size": [4, 4, 4], - "uv": { - "north": {"uv": [24, 64], "uv_size": [8, 8]}, - "east": {"uv": [64, 24], "uv_size": [8, 8]}, - "south": {"uv": [64, 32], "uv_size": [8, 8]}, - "west": {"uv": [0, 66], "uv_size": [8, 8]}, - "up": {"uv": [66, 0], "uv_size": [8, 8]}, - "down": {"uv": [66, 58], "uv_size": [8, -8]} - } - } - ] - }, - { - "name": "cloud144", - "parent": "cluster32", - "pivot": [0, 64, 0.5], - "cubes": [ - { - "origin": [-3, 61, -2.5], - "size": [6, 6, 6], - "uv": { - "north": {"uv": [12, 24], "uv_size": [12, 12]}, - "east": {"uv": [24, 12], "uv_size": [12, 12]}, - "south": {"uv": [24, 24], "uv_size": [12, 12]}, - "west": {"uv": [0, 36], "uv_size": [12, 12]}, - "up": {"uv": [36, 0], "uv_size": [12, 12]}, - "down": {"uv": [12, 48], "uv_size": [12, -12]} - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/geo/item/doomicorn.geo.json b/common/src/main/resources/assets/azurelib/geo/item/doomicorn.geo.json deleted file mode 100644 index 2f4ec1a72..000000000 --- a/common/src/main/resources/assets/azurelib/geo/item/doomicorn.geo.json +++ /dev/null @@ -1,17113 +0,0 @@ -{ - "format_version": "1.12.0", - "minecraft:geometry": [ - { - "description": { - "identifier": "geometry.unknown", - "texture_width": 64, - "texture_height": 64, - "visible_bounds_width": 4, - "visible_bounds_height": 4.5, - "visible_bounds_offset": [ - 0, - 1.75, - 0 - ] - }, - "bones": [ - { - "name": "bipedHead", - "pivot": [ - 0, - 24, - 0 - ] - }, - { - "name": "armorHead", - "parent": "bipedHead", - "pivot": [ - 0, - 24, - 0 - ] - }, - { - "name": "horn", - "parent": "armorHead", - "pivot": [ - 0, - 33, - -3 - ] - }, - { - "name": "group3", - "parent": "horn", - "pivot": [ - 8, - 34.7, - -12 - ], - "cubes": [ - { - "origin": [ - -0.75, - 38, - -5.75 - ], - "size": [ - 1.5, - 2, - 1.5 - ], - "pivot": [ - 0, - 38.7, - -5 - ], - "rotation": [ - 22.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 25, - 23 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 25, - 23 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 24, - 24 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 24, - 25 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 24, - 25 - ], - "uv_size": [ - 1, - 1 - ] - } - } - }, - { - "origin": [ - -1, - 35, - -6 - ], - "size": [ - 2, - 3, - 2 - ], - "pivot": [ - 0, - 38.7, - -5 - ], - "rotation": [ - 22.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 23, - 23 - ], - "uv_size": [ - 2, - 3 - ] - }, - "east": { - "uv": [ - 23, - 24 - ], - "uv_size": [ - 2, - 3 - ] - }, - "south": { - "uv": [ - 23, - 24 - ], - "uv_size": [ - 2, - 3 - ] - }, - "west": { - "uv": [ - 23, - 22 - ], - "uv_size": [ - 2, - 3 - ] - }, - "up": { - "uv": [ - 23, - 23 - ], - "uv_size": [ - 2, - 2 - ] - } - } - }, - { - "origin": [ - -0.4, - 40, - -5.4 - ], - "size": [ - 0.8, - 1.2, - 0.8 - ], - "pivot": [ - 0, - 38.7, - -5 - ], - "rotation": [ - 22.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 24, - 23 - ], - "uv_size": [ - 0, - 1 - ] - }, - "east": { - "uv": [ - 25, - 25 - ], - "uv_size": [ - 0, - 1 - ] - }, - "south": { - "uv": [ - 25, - 25 - ], - "uv_size": [ - 0, - 1 - ] - }, - "west": { - "uv": [ - 25, - 23 - ], - "uv_size": [ - 0, - 1 - ] - }, - "up": { - "uv": [ - 26, - 26 - ], - "uv_size": [ - 0, - 0 - ] - } - } - } - ] - }, - { - "name": "bone2", - "parent": "horn", - "pivot": [ - 8, - 31.7, - -16.5 - ], - "cubes": [ - { - "origin": [ - -2.5, - 32.7, - -6 - ], - "size": [ - 5, - 3, - 7 - ], - "uv": { - "north": { - "uv": [ - 10, - 3 - ], - "uv_size": [ - 5, - 3 - ] - }, - "east": { - "uv": [ - 7, - 0 - ], - "uv_size": [ - -7, - 3 - ] - }, - "south": { - "uv": [ - 10, - 4 - ], - "uv_size": [ - 5, - 3 - ] - }, - "west": { - "uv": [ - 0, - 0 - ], - "uv_size": [ - 7, - 3 - ] - }, - "up": { - "uv": [ - 9, - 1 - ], - "uv_size": [ - 5, - 7 - ] - }, - "down": { - "uv": [ - 9, - 8 - ], - "uv_size": [ - 5, - -7 - ] - } - } - }, - { - "origin": [ - -0.5, - 35.7, - -2.3 - ], - "size": [ - 1, - 1.2, - 3.3 - ], - "uv": { - "north": { - "uv": [ - 21, - 23 - ], - "uv_size": [ - 5, - 3 - ] - }, - "east": { - "uv": [ - 23, - 24 - ], - "uv_size": [ - 3, - 3 - ] - }, - "south": { - "uv": [ - 22, - 23 - ], - "uv_size": [ - 5, - 3 - ] - }, - "west": { - "uv": [ - 22, - 23 - ], - "uv_size": [ - 5, - 3 - ] - }, - "up": { - "uv": [ - 22, - 22 - ], - "uv_size": [ - 4, - 5 - ] - } - } - }, - { - "origin": [ - -0.5, - 33, - -2.1 - ], - "size": [ - 1, - 2.2, - 5.5 - ], - "pivot": [ - -2.5, - 32.5, - 1.4 - ], - "rotation": [ - -47.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 21, - 23 - ], - "uv_size": [ - 5, - 3 - ] - }, - "east": { - "uv": [ - 23, - 24 - ], - "uv_size": [ - 3, - 3 - ] - }, - "west": { - "uv": [ - 22, - 23 - ], - "uv_size": [ - 5, - 3 - ] - } - } - }, - { - "origin": [ - 0.6, - 34.8, - -0.9 - ], - "size": [ - 1.8, - 2.8, - 0.8 - ], - "uv": { - "north": { - "uv": [ - 10, - 3 - ], - "uv_size": [ - 2, - 3 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 3 - ] - }, - "south": { - "uv": [ - 11, - 2 - ], - "uv_size": [ - 2, - 3 - ] - }, - "west": { - "uv": [ - 14, - 3 - ], - "uv_size": [ - 1, - 3 - ] - }, - "up": { - "uv": [ - 10, - 3 - ], - "uv_size": [ - 2, - 1 - ] - } - } - }, - { - "origin": [ - -2.4, - 34.8, - -0.9 - ], - "size": [ - 1.8, - 2.8, - 0.8 - ], - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 2, - 3 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 3 - ] - }, - "south": { - "uv": [ - 11, - 2 - ], - "uv_size": [ - 2, - 3 - ] - }, - "west": { - "uv": [ - 12, - 2 - ], - "uv_size": [ - 1, - 3 - ] - }, - "up": { - "uv": [ - 10, - 3 - ], - "uv_size": [ - 2, - 1 - ] - } - } - }, - { - "origin": [ - -2, - 32.7, - -8 - ], - "size": [ - 4, - 3, - 2 - ], - "uv": { - "north": { - "uv": [ - 10, - 3 - ], - "uv_size": [ - 4, - 3 - ] - }, - "east": { - "uv": [ - 12, - 3 - ], - "uv_size": [ - 2, - 3 - ] - }, - "west": { - "uv": [ - 12, - 2 - ], - "uv_size": [ - 2, - 3 - ] - }, - "up": { - "uv": [ - 11, - 4 - ], - "uv_size": [ - 4, - 2 - ] - }, - "down": { - "uv": [ - 10, - 5 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - -1, - 32.7, - 0.5 - ], - "size": [ - 2, - 1.9, - 2 - ], - "uv": { - "east": { - "uv": [ - 11, - 0 - ], - "uv_size": [ - 2, - 8 - ] - }, - "south": { - "uv": [ - 11, - 0 - ], - "uv_size": [ - 2, - 8 - ] - }, - "west": { - "uv": [ - 11, - 0 - ], - "uv_size": [ - 2, - 8 - ] - }, - "up": { - "uv": [ - 13, - 3 - ], - "uv_size": [ - 2, - 2 - ] - } - } - } - ] - }, - { - "name": "bone5", - "parent": "armorHead", - "pivot": [ - 0, - 0, - 0 - ], - "cubes": [ - { - "origin": [ - 2, - 24, - -5 - ], - "size": [ - 3, - 2, - 1 - ], - "uv": { - "north": { - "uv": [ - 46, - 14 - ], - "uv_size": [ - 3, - 2 - ] - }, - "west": { - "uv": [ - 48, - 14 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 41, - 14 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 46, - 16 - ], - "uv_size": [ - 3, - -1 - ] - } - } - }, - { - "origin": [ - -5, - 24, - -5 - ], - "size": [ - 3, - 2, - 1 - ], - "uv": { - "north": { - "uv": [ - 39, - 14 - ], - "uv_size": [ - 3, - 2 - ] - }, - "east": { - "uv": [ - 39, - 14 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 41, - 14 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 39, - 16 - ], - "uv_size": [ - 3, - -1 - ] - } - } - }, - { - "origin": [ - -5, - 26, - -5 - ], - "size": [ - 2, - 1, - 1 - ], - "uv": { - "north": { - "uv": [ - 39, - 13 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 39, - 13 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 39, - 13 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 41, - 14 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -5, - 27, - -5 - ], - "size": [ - 1, - 3, - 1 - ], - "uv": { - "north": { - "uv": [ - 39, - 10 - ], - "uv_size": [ - 1, - 3 - ] - }, - "east": { - "uv": [ - 39, - 10 - ], - "uv_size": [ - 1, - 3 - ] - }, - "west": { - "uv": [ - 39, - 10 - ], - "uv_size": [ - 1, - 3 - ] - } - } - }, - { - "origin": [ - 4, - 27, - -5 - ], - "size": [ - 1, - 3, - 1 - ], - "uv": { - "north": { - "uv": [ - 39, - 10 - ], - "uv_size": [ - 1, - 3 - ] - }, - "east": { - "uv": [ - 39, - 10 - ], - "uv_size": [ - 1, - 3 - ] - }, - "west": { - "uv": [ - 48, - 10 - ], - "uv_size": [ - 1, - 3 - ] - } - } - }, - { - "origin": [ - -5, - 30, - -5 - ], - "size": [ - 10, - 1, - 1 - ], - "uv": { - "north": { - "uv": [ - 39, - 9 - ], - "uv_size": [ - 10, - 1 - ] - }, - "east": { - "uv": [ - 39, - 9 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 48, - 9 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 49, - 10 - ], - "uv_size": [ - -10, - -1 - ] - } - } - }, - { - "origin": [ - -5, - 31, - -5 - ], - "size": [ - 10, - 2, - 1 - ], - "uv": { - "north": { - "uv": [ - 59, - 8 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 59, - 8 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 59, - 8 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 61, - 9 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -2, - 29, - -5 - ], - "size": [ - 4, - 1, - 1 - ], - "uv": { - "north": { - "uv": [ - 42, - 10 - ], - "uv_size": [ - 4, - 1 - ] - }, - "east": { - "uv": [ - 42, - 10 - ], - "uv_size": [ - 4, - 1 - ] - }, - "west": { - "uv": [ - 42, - 10 - ], - "uv_size": [ - 4, - 1 - ] - }, - "down": { - "uv": [ - 46, - 11 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - 3, - 26, - -5 - ], - "size": [ - 2, - 1, - 1 - ], - "uv": { - "north": { - "uv": [ - 47, - 13 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 39, - 13 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 48, - 13 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 49, - 14 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -2, - 24, - -5 - ], - "size": [ - 4, - 2, - 9 - ], - "uv": { - "north": { - "uv": [ - 42, - 14 - ], - "uv_size": [ - 4, - 2 - ] - }, - "up": { - "uv": [ - 41, - 14 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 46, - 16 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - -1, - 26, - -5 - ], - "size": [ - 2, - 1, - 1 - ], - "uv": { - "north": { - "uv": [ - 43, - 13 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 8, - 6 - ], - "uv_size": [ - 8, - 1 - ] - }, - "west": { - "uv": [ - 8, - 6 - ], - "uv_size": [ - 8, - 1 - ] - }, - "up": { - "uv": [ - 16, - 7 - ], - "uv_size": [ - -8, - -1 - ] - } - } - }, - { - "origin": [ - -4, - 27, - -4.5 - ], - "size": [ - 8, - 2, - 0.1 - ], - "uv": { - "north": { - "uv": [ - 40, - 11 - ], - "uv_size": [ - 8, - 2 - ] - }, - "south": { - "uv": [ - 42, - 11 - ], - "uv_size": [ - 4, - 2 - ] - } - } - }, - { - "origin": [ - 1, - 26, - -4.5 - ], - "size": [ - 2, - 1, - 0.1 - ], - "uv": { - "north": { - "uv": [ - 45, - 13 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 42, - 11 - ], - "uv_size": [ - 4, - 2 - ] - } - } - }, - { - "origin": [ - -3, - 26, - -4.5 - ], - "size": [ - 2, - 1, - 0.1 - ], - "uv": { - "north": { - "uv": [ - 41, - 13 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 42, - 11 - ], - "uv_size": [ - 4, - 2 - ] - } - } - }, - { - "origin": [ - -4, - 29, - -4.5 - ], - "size": [ - 2, - 1, - 0.1 - ], - "uv": { - "north": { - "uv": [ - 40, - 10 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 42, - 11 - ], - "uv_size": [ - 4, - 2 - ] - } - } - }, - { - "origin": [ - 2, - 29, - -4.5 - ], - "size": [ - 2, - 1, - 0.1 - ], - "uv": { - "north": { - "uv": [ - 46, - 10 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 42, - 11 - ], - "uv_size": [ - 4, - 2 - ] - } - } - }, - { - "origin": [ - -5, - 32, - -2 - ], - "size": [ - 10, - 1, - 6 - ], - "uv": { - "east": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "west": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "up": { - "uv": [ - 48, - 7 - ], - "uv_size": [ - -8, - -6 - ] - } - } - }, - { - "origin": [ - -5, - 32, - -4 - ], - "size": [ - 10, - 1, - 2 - ], - "uv": { - "east": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "west": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "up": { - "uv": [ - 11, - 6 - ], - "uv_size": [ - -3, - -5 - ] - } - } - }, - { - "origin": [ - -5, - 25, - 4 - ], - "size": [ - 10, - 1, - 1 - ], - "pivot": [ - 5, - 25, - 4 - ], - "rotation": [ - -90, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "east": { - "uv": [ - 54, - 15 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "west": { - "uv": [ - 54, - 15 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 59, - 16 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -5, - 33, - 4 - ], - "size": [ - 10, - 1, - 1 - ], - "pivot": [ - 5, - 33, - 4 - ], - "rotation": [ - -90, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "east": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "west": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "up": { - "uv": [ - 61, - 9 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 4, - 32, - 4 - ], - "size": [ - 1, - 1, - 7 - ], - "pivot": [ - 5, - 32, - 4 - ], - "rotation": [ - -90, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "west": { - "uv": [ - 57, - 15 - ], - "uv_size": [ - 7, - 1 - ] - }, - "up": { - "uv": [ - 58, - 16 - ], - "uv_size": [ - -1, - -7 - ] - } - } - }, - { - "origin": [ - -5, - 32, - 4 - ], - "size": [ - 1, - 1, - 7 - ], - "pivot": [ - -4, - 32, - 4 - ], - "rotation": [ - -90, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 8, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "east": { - "uv": [ - 57, - 15 - ], - "uv_size": [ - 7, - 1 - ] - }, - "up": { - "uv": [ - 63, - 16 - ], - "uv_size": [ - -1, - -7 - ] - } - } - }, - { - "origin": [ - -4, - 32, - 4 - ], - "size": [ - 8, - 1, - 7 - ], - "pivot": [ - 3.5, - 32, - 4 - ], - "rotation": [ - -90, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 40, - 1 - ], - "uv_size": [ - 3, - 5 - ] - }, - "up": { - "uv": [ - 63, - 16 - ], - "uv_size": [ - -6, - -7 - ] - } - } - }, - { - "origin": [ - 2, - 24, - -4 - ], - "size": [ - 3, - 6, - 7 - ], - "uv": { - "west": { - "uv": [ - 49, - 10 - ], - "uv_size": [ - 6, - 6 - ] - }, - "down": { - "uv": [ - 54, - 14 - ], - "uv_size": [ - -5, - -4 - ] - } - } - }, - { - "origin": [ - -5, - 24, - -4 - ], - "size": [ - 3, - 6, - 7 - ], - "uv": { - "east": { - "uv": [ - 32, - 10 - ], - "uv_size": [ - 7, - 6 - ] - }, - "down": { - "uv": [ - 49, - 14 - ], - "uv_size": [ - 5, - -4 - ] - } - } - }, - { - "origin": [ - 4, - 30, - -4 - ], - "size": [ - 1, - 2, - 7 - ], - "uv": { - "west": { - "uv": [ - 49, - 8 - ], - "uv_size": [ - 6, - 2 - ] - } - } - }, - { - "origin": [ - -5, - 30, - -4 - ], - "size": [ - 1, - 2, - 7 - ], - "uv": { - "east": { - "uv": [ - 55, - 8 - ], - "uv_size": [ - -6, - 2 - ] - } - } - }, - { - "origin": [ - 2, - 24, - 3 - ], - "size": [ - 3, - 6, - 1 - ], - "uv": { - "south": { - "uv": [ - 0, - 0 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 54, - 10 - ], - "uv_size": [ - 1, - 6 - ] - }, - "down": { - "uv": [ - 46, - 16 - ], - "uv_size": [ - 3, - -1 - ] - } - } - }, - { - "origin": [ - -5, - 24, - 3 - ], - "size": [ - 3, - 6, - 1 - ], - "uv": { - "east": { - "uv": [ - 54, - 10 - ], - "uv_size": [ - 1, - 6 - ] - }, - "south": { - "uv": [ - 0, - 0 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 46, - 16 - ], - "uv_size": [ - 3, - -1 - ] - } - } - }, - { - "origin": [ - 4, - 30, - 3 - ], - "size": [ - 1, - 2, - 1 - ], - "uv": { - "west": { - "uv": [ - 54, - 10 - ], - "uv_size": [ - 1, - 2 - ] - } - } - }, - { - "origin": [ - -5, - 30, - 3 - ], - "size": [ - 1, - 2, - 1 - ], - "uv": { - "east": { - "uv": [ - 54, - 10 - ], - "uv_size": [ - 1, - 2 - ] - } - } - } - ] - }, - { - "name": "bipedBody", - "pivot": [ - 0, - 24, - 0 - ] - }, - { - "name": "armorBody", - "parent": "bipedBody", - "pivot": [ - 0, - 24, - 0 - ] - }, - { - "name": "bone", - "parent": "armorBody", - "pivot": [ - 0, - 21.3, - 3.1 - ] - }, - { - "name": "group", - "parent": "bone", - "pivot": [ - 0.10714, - 20.31429, - 3.4 - ], - "cubes": [ - { - "origin": [ - -0.35, - 20.5, - 2.3 - ], - "size": [ - 12.1, - 1.6, - 1.6 - ], - "inflate": 0.1, - "pivot": [ - 1.2, - 21.3, - 3.1 - ], - "rotation": [ - 0, - 0, - -22.5 - ], - "uv": { - "north": { - "uv": [ - 22, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "east": { - "uv": [ - 23, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 22, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 23, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 22, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "down": { - "uv": [ - 22, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 5.67, - 21.27, - 2.32 - ], - "size": [ - 7.56, - 1.56, - 1.56 - ], - "inflate": 0.06, - "pivot": [ - 7.2, - 21.3, - 3.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 25, - 43 - ], - "uv_size": [ - -4, - 2 - ] - }, - "east": { - "uv": [ - 21, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 22, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "down": { - "uv": [ - 21, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 10.155, - 23.505, - 2.305 - ], - "size": [ - 9.09, - 1.59, - 1.59 - ], - "inflate": 0.09, - "pivot": [ - 11.7, - 24.3, - 3.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 25, - 43 - ], - "uv_size": [ - -4, - 2 - ] - }, - "south": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 21, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "down": { - "uv": [ - 21, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 8.66, - 22.76, - 2.31 - ], - "size": [ - 9.08, - 1.58, - 1.58 - ], - "inflate": 0.08, - "pivot": [ - 10.2, - 22.8, - 3.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 25, - 43 - ], - "uv_size": [ - -4, - 2 - ] - }, - "east": { - "uv": [ - 21, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 22, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 21, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 2.68, - 19.78, - 2.33 - ], - "size": [ - 6.04, - 1.54, - 1.54 - ], - "inflate": 0.04, - "pivot": [ - 4.2, - 19.8, - 3.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 25, - 43 - ], - "uv_size": [ - -4, - 2 - ] - }, - "east": { - "uv": [ - 21, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 22, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "down": { - "uv": [ - 21, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 7.165, - 22.015, - 2.315 - ], - "size": [ - 7.57, - 1.57, - 1.57 - ], - "inflate": 0.07, - "pivot": [ - 8.7, - 21.3, - 3.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 25, - 43 - ], - "uv_size": [ - -4, - 2 - ] - }, - "east": { - "uv": [ - 20, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 22, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 21, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 4.175, - 20.525, - 2.325 - ], - "size": [ - 6.05, - 1.55, - 1.55 - ], - "inflate": 0.05, - "pivot": [ - 5.7, - 19.8, - 3.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 25, - 43 - ], - "uv_size": [ - -4, - 2 - ] - }, - "east": { - "uv": [ - 22, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 22, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "down": { - "uv": [ - 21, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - } - ] - }, - { - "name": "group2", - "parent": "bone", - "pivot": [ - -0.19286, - 20.31429, - 3.6 - ], - "rotation": [ - -180, - 0, - 180 - ], - "cubes": [ - { - "origin": [ - -0.65, - 20.5, - 3.3 - ], - "size": [ - 12.1, - 1.6, - 1.6 - ], - "inflate": 0.1, - "pivot": [ - 0.9, - 21.3, - 4.1 - ], - "rotation": [ - 0, - 0, - -22.5 - ], - "uv": { - "north": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "east": { - "uv": [ - 25, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 25, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "down": { - "uv": [ - 23, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 5.37, - 21.27, - 3.32 - ], - "size": [ - 7.56, - 1.56, - 1.56 - ], - "inflate": 0.06, - "pivot": [ - 6.9, - 21.3, - 4.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "east": { - "uv": [ - 23, - 23 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 21, - 24 - ], - "uv_size": [ - 5, - 1 - ] - }, - "down": { - "uv": [ - 22, - 25 - ], - "uv_size": [ - 5, - -1 - ] - } - } - }, - { - "origin": [ - 9.855, - 23.505, - 3.305 - ], - "size": [ - 9.09, - 1.59, - 1.59 - ], - "inflate": 0.09, - "pivot": [ - 11.4, - 24.3, - 4.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "south": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 26, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "down": { - "uv": [ - 23, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 8.36, - 22.76, - 3.31 - ], - "size": [ - 9.08, - 1.58, - 1.58 - ], - "inflate": 0.08, - "pivot": [ - 9.9, - 22.8, - 4.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "east": { - "uv": [ - 23, - 23 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 24, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 21, - 25 - ], - "uv_size": [ - 6, - -1 - ] - } - } - }, - { - "origin": [ - 2.38, - 19.78, - 3.33 - ], - "size": [ - 6.04, - 1.54, - 1.54 - ], - "inflate": 0.04, - "pivot": [ - 3.9, - 19.8, - 4.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "east": { - "uv": [ - 26, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 22, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "down": { - "uv": [ - 23, - 45 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 6.865, - 22.015, - 3.315 - ], - "size": [ - 7.57, - 1.57, - 1.57 - ], - "inflate": 0.07, - "pivot": [ - 8.4, - 21.3, - 4.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "east": { - "uv": [ - 23, - 23 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 24, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 21, - 25 - ], - "uv_size": [ - 5, - -1 - ] - } - } - }, - { - "origin": [ - 3.875, - 20.525, - 3.325 - ], - "size": [ - 6.05, - 1.55, - 1.55 - ], - "inflate": 0.05, - "pivot": [ - 5.4, - 19.8, - 4.1 - ], - "rotation": [ - 0, - 0, - 22.5 - ], - "uv": { - "north": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "east": { - "uv": [ - 23, - 25 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 23, - 43 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 25, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 22, - 24 - ], - "uv_size": [ - 4, - 1 - ] - }, - "down": { - "uv": [ - 22, - 25 - ], - "uv_size": [ - 4, - -1 - ] - } - } - } - ] - }, - { - "name": "bone3", - "parent": "armorBody", - "pivot": [ - 0, - 24, - 0 - ], - "cubes": [ - { - "origin": [ - -4, - 12, - -2 - ], - "size": [ - 8, - 12, - 4 - ], - "uv": { - "north": { - "uv": [ - 20, - 20 - ], - "uv_size": [ - 8, - 12 - ] - }, - "south": { - "uv": [ - 32, - 20 - ], - "uv_size": [ - 8, - 12 - ] - } - } - } - ] - }, - { - "name": "bone15", - "parent": "bone3", - "pivot": [ - 0, - 18, - -2.3 - ], - "cubes": [ - { - "origin": [ - -4.1, - 12, - -2.4 - ], - "size": [ - 8.2, - 3, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 20, - 45 - ], - "uv_size": [ - 8, - 3 - ] - }, - "east": { - "uv": [ - 20, - 45 - ], - "uv_size": [ - 1, - 3 - ] - }, - "west": { - "uv": [ - 27, - 45 - ], - "uv_size": [ - 1, - 3 - ] - }, - "up": { - "uv": [ - 20, - 38 - ], - "uv_size": [ - 8, - 1 - ] - }, - "down": { - "uv": [ - 20, - 47 - ], - "uv_size": [ - 8, - 1 - ] - } - } - }, - { - "origin": [ - -4.1, - 12, - 2.1 - ], - "size": [ - 8.2, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 32, - 47 - ], - "uv_size": [ - 8, - 1 - ] - }, - "east": { - "uv": [ - 32, - 47 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 32, - 47 - ], - "uv_size": [ - 8, - 1 - ] - }, - "west": { - "uv": [ - 32, - 47 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 40, - 48 - ], - "uv_size": [ - -8, - -1 - ] - }, - "down": { - "uv": [ - 40, - 48 - ], - "uv_size": [ - -8, - -1 - ] - } - } - }, - { - "origin": [ - 1, - 13, - 2.1 - ], - "size": [ - 3.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 46 - ], - "uv_size": [ - -3, - 1 - ] - }, - "east": { - "uv": [ - 34, - 46 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 32, - 46 - ], - "uv_size": [ - 3, - 1 - ] - }, - "west": { - "uv": [ - 32, - 47 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 35, - 47 - ], - "uv_size": [ - -3, - -1 - ] - } - } - }, - { - "origin": [ - -4.1, - 13, - 2.1 - ], - "size": [ - 3.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 32, - 46 - ], - "uv_size": [ - 3, - 1 - ] - }, - "east": { - "uv": [ - 32, - 47 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - 3, - 1 - ] - }, - "west": { - "uv": [ - 34, - 46 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 40, - 47 - ], - "uv_size": [ - -3, - -1 - ] - } - } - }, - { - "origin": [ - -1, - 14, - 2.1 - ], - "size": [ - 2.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -1, - 16, - 2.1 - ], - "size": [ - 2.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 1, - 17, - 2.1 - ], - "size": [ - 1.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -2, - 17, - 2.1 - ], - "size": [ - 1.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 1, - 19, - 2.1 - ], - "size": [ - 1.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 32, - 41 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 32, - 41 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 32, - 41 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 32, - 41 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 33, - 42 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -1, - 18, - 2.1 - ], - "size": [ - 2.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -4, - 20, - 2.1 - ], - "size": [ - 8.1, - 2, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 32, - 38 - ], - "uv_size": [ - 8, - 2 - ] - }, - "east": { - "uv": [ - 39, - 38 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 32, - 38 - ], - "uv_size": [ - 8, - 2 - ] - }, - "west": { - "uv": [ - 39, - 38 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 40, - 39 - ], - "uv_size": [ - -8, - -1 - ] - }, - "down": { - "uv": [ - 40, - 40 - ], - "uv_size": [ - -8, - -1 - ] - } - } - }, - { - "origin": [ - -2.1, - 23, - 2.1 - ], - "size": [ - 4.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 34, - 36 - ], - "uv_size": [ - 4, - 1 - ] - }, - "east": { - "uv": [ - 39, - 38 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 34, - 36 - ], - "uv_size": [ - 4, - 1 - ] - }, - "west": { - "uv": [ - 39, - 38 - ], - "uv_size": [ - 1, - 2 - ] - } - } - }, - { - "origin": [ - -3.1, - 22, - 2.1 - ], - "size": [ - 6.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 33, - 37 - ], - "uv_size": [ - 6, - 1 - ] - }, - "east": { - "uv": [ - 39, - 38 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 33, - 37 - ], - "uv_size": [ - 6, - 1 - ] - }, - "west": { - "uv": [ - 39, - 38 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 39, - 38 - ], - "uv_size": [ - -6, - -1 - ] - } - } - }, - { - "origin": [ - -2, - 19, - 2.1 - ], - "size": [ - 2.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 36, - 40 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 37, - 40 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 36, - 40 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 36, - 40 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 38, - 41 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 3, - 18, - 2.1 - ], - "size": [ - 1.1, - 2, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 32, - 40 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 32, - 40 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 32, - 40 - ], - "uv_size": [ - 1, - 2 - ] - }, - "down": { - "uv": [ - 33, - 42 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -4, - 18, - 2.1 - ], - "size": [ - 1.1, - 2, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 39, - 40 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 39, - 40 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 39, - 40 - ], - "uv_size": [ - 1, - 2 - ] - }, - "down": { - "uv": [ - 40, - 42 - ], - "uv_size": [ - -1, - -2 - ] - } - } - }, - { - "origin": [ - -3, - 15, - 2.1 - ], - "size": [ - 2.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 38, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 37, - 44 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 39, - 45 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 1, - 15, - 2.1 - ], - "size": [ - 2.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 33, - 44 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 33, - 44 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 35, - 45 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 37, - 46 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -3.1, - 15, - -2.1 - ], - "size": [ - 6.2, - 2, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 6, - 2 - ] - }, - "east": { - "uv": [ - 26, - 43 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 26, - 43 - ], - "uv_size": [ - 1, - 2 - ] - } - } - }, - { - "origin": [ - -3.1, - 22, - -2.4 - ], - "size": [ - 6.2, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 21, - 37 - ], - "uv_size": [ - 6, - 1 - ] - }, - "east": { - "uv": [ - 21, - 37 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 21, - 37 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 27, - 38 - ], - "uv_size": [ - -6, - -1 - ] - } - } - }, - { - "origin": [ - 1, - 23, - -2.4 - ], - "size": [ - 1.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 22, - 36 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 22, - 36 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 22, - 36 - ], - "uv_size": [ - 1, - 1 - ] - } - } - }, - { - "origin": [ - 2, - 20, - -2.7 - ], - "size": [ - 1.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 26, - 39 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 26, - 39 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 26, - 39 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 27, - 40 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 27, - 40 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -2.1, - 23, - -2.4 - ], - "size": [ - 1.1, - 1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 22, - 36 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 22, - 36 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 22, - 36 - ], - "uv_size": [ - 1, - 1 - ] - } - } - }, - { - "origin": [ - -4.1, - 17, - -2.4 - ], - "size": [ - 8.2, - 5, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 20, - 38 - ], - "uv_size": [ - 8, - 5 - ] - }, - "east": { - "uv": [ - 20, - 38 - ], - "uv_size": [ - 1, - 5 - ] - }, - "west": { - "uv": [ - 27, - 38 - ], - "uv_size": [ - 1, - 5 - ] - }, - "up": { - "uv": [ - 20, - 38 - ], - "uv_size": [ - 8, - 1 - ] - }, - "down": { - "uv": [ - 20, - 47 - ], - "uv_size": [ - 8, - 1 - ] - } - } - } - ] - }, - { - "name": "bipedRightArm", - "pivot": [ - -4, - 22, - 0 - ] - }, - { - "name": "armorRightArm", - "parent": "bipedRightArm", - "pivot": [ - -4, - 22, - 0 - ] - }, - { - "name": "bone11", - "parent": "armorRightArm", - "pivot": [ - -4, - 22, - 0 - ], - "cubes": [ - { - "origin": [ - -8.1, - 11.9, - -2.1 - ], - "size": [ - 4.2, - 12.1, - 4.2 - ], - "uv": { - "north": { - "uv": [ - 44, - 20 - ], - "uv_size": [ - 4, - 12 - ] - }, - "east": { - "uv": [ - 40, - 20 - ], - "uv_size": [ - 4, - 12 - ] - }, - "south": { - "uv": [ - 52, - 20 - ], - "uv_size": [ - 4, - 12 - ] - }, - "west": { - "uv": [ - 48, - 20 - ], - "uv_size": [ - 4, - 12 - ] - }, - "up": { - "uv": [ - 44, - 20 - ], - "uv_size": [ - 4, - -4 - ] - } - } - } - ] - }, - { - "name": "armorRightOut", - "parent": "bone11", - "pivot": [ - -7.1, - 24.1, - 2 - ], - "cubes": [ - { - "origin": [ - -8.45, - 20, - -2.25 - ], - "size": [ - 0.35, - 3.1, - 4.5 - ], - "uv": { - "north": { - "uv": [ - 43, - 37 - ], - "uv_size": [ - 1, - 3 - ] - }, - "east": { - "uv": [ - 40, - 37 - ], - "uv_size": [ - 4, - 3 - ] - }, - "south": { - "uv": [ - 43, - 37 - ], - "uv_size": [ - 1, - 3 - ] - }, - "west": { - "uv": [ - 40, - 37 - ], - "uv_size": [ - 4, - 3 - ] - }, - "up": { - "uv": [ - 44, - 40 - ], - "uv_size": [ - -1, - -3 - ] - }, - "down": { - "uv": [ - 44, - 40 - ], - "uv_size": [ - -1, - -3 - ] - } - } - }, - { - "origin": [ - -8.45, - 17, - 1.15 - ], - "size": [ - 0.35, - 1, - 1.1 - ], - "uv": { - "north": { - "uv": [ - 40, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 40, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 40, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 41, - 43 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -3.95, - 16, - 1.15 - ], - "size": [ - 0.35, - 1, - 1.1 - ], - "uv": { - "east": { - "uv": [ - 51, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 51, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 51, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 52, - 44 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -8.45, - 23.1, - -1.15 - ], - "size": [ - 0.35, - 0.9, - 2.3 - ], - "uv": { - "north": { - "uv": [ - 41, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 41, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 41, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 43, - 37 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -7.25, - 23, - 2.55 - ], - "size": [ - 0.35, - 1.1, - 2.3 - ], - "pivot": [ - -7.25, - 23, - 2.45 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 53, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 53, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 53, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 55, - 37 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 55, - 37 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -8.35, - 20.05, - 2.55 - ], - "size": [ - 0.25, - 2.95, - 1.2 - ], - "pivot": [ - -8.35, - 22, - 2.45 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 53, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 53, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 55, - 37 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 55, - 37 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -8.35, - 16, - 2.55 - ], - "size": [ - 0.35, - 2, - 2.3 - ], - "pivot": [ - -8.35, - 17, - 2.45 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 54, - 42 - ], - "uv_size": [ - 2, - 2 - ] - }, - "east": { - "uv": [ - 54, - 42 - ], - "uv_size": [ - 2, - 2 - ] - }, - "up": { - "uv": [ - 56, - 44 - ], - "uv_size": [ - -2, - -2 - ] - }, - "down": { - "uv": [ - 56, - 44 - ], - "uv_size": [ - -2, - -2 - ] - } - } - }, - { - "origin": [ - -6.25, - 16, - 2.75 - ], - "size": [ - 0.35, - 1, - 2.1 - ], - "pivot": [ - -6.25, - 17, - 2.45 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 52, - 43 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 52, - 43 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 54, - 44 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 54, - 44 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - -8.25, - 11.9, - 2.55 - ], - "size": [ - 0.35, - 4.1, - 4.3 - ], - "pivot": [ - -8.25, - 11.9, - 2.45 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 61, - 60 - ], - "uv_size": [ - -1, - 4 - ] - }, - "east": { - "uv": [ - 64, - 60 - ], - "uv_size": [ - -4, - 4 - ] - }, - "up": { - "uv": [ - 61, - 60 - ], - "uv_size": [ - -1, - 4 - ] - }, - "down": { - "uv": [ - 61, - 53 - ], - "uv_size": [ - 2, - -1 - ] - } - } - }, - { - "origin": [ - -7.35, - 23.1, - -2.05 - ], - "size": [ - 0.35, - 0.9, - 2.3 - ], - "pivot": [ - -7.25, - 23, - -2.15 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 55, - 52 - ], - "uv_size": [ - -1, - 1 - ] - }, - "east": { - "uv": [ - 45, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 54, - 52 - ], - "uv_size": [ - -1, - 1 - ] - }, - "west": { - "uv": [ - 45, - 36 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 53, - 53 - ], - "uv_size": [ - 1, - -1 - ] - }, - "down": { - "uv": [ - 53, - 53 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -8.45, - 16, - -2.05 - ], - "size": [ - 0.35, - 1, - 3.4 - ], - "pivot": [ - -8.35, - 16, - -2.15 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 56, - 59 - ], - "uv_size": [ - -2, - 1 - ] - }, - "east": { - "uv": [ - 56, - 59 - ], - "uv_size": [ - -2, - 1 - ] - }, - "south": { - "uv": [ - 56, - 59 - ], - "uv_size": [ - -2, - 1 - ] - }, - "west": { - "uv": [ - 44, - 43 - ], - "uv_size": [ - 3, - 1 - ] - }, - "up": { - "uv": [ - 54, - 60 - ], - "uv_size": [ - 2, - -1 - ] - } - } - }, - { - "origin": [ - -8.45, - 21, - -2.05 - ], - "size": [ - 0.35, - 2.1, - 4.5 - ], - "pivot": [ - -8.35, - 22, - -2.15 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 55, - 52 - ], - "uv_size": [ - -1, - 1 - ] - }, - "east": { - "uv": [ - 48, - 37 - ], - "uv_size": [ - -4, - 2 - ] - }, - "south": { - "uv": [ - 54, - 52 - ], - "uv_size": [ - -1, - 1 - ] - }, - "west": { - "uv": [ - 44, - 37 - ], - "uv_size": [ - 4, - 2 - ] - }, - "up": { - "uv": [ - 53, - 53 - ], - "uv_size": [ - 1, - -1 - ] - }, - "down": { - "uv": [ - 53, - 53 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -8.45, - 20, - -2.05 - ], - "size": [ - 0.35, - 1, - 3.4 - ], - "pivot": [ - -8.35, - 21, - -2.15 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 55, - 52 - ], - "uv_size": [ - -1, - 1 - ] - }, - "east": { - "uv": [ - 52, - 53 - ], - "uv_size": [ - 4, - 2 - ] - }, - "south": { - "uv": [ - 54, - 52 - ], - "uv_size": [ - -1, - 1 - ] - }, - "west": { - "uv": [ - 44, - 39 - ], - "uv_size": [ - 3, - 1 - ] - }, - "down": { - "uv": [ - 53, - 53 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -8.45, - 11.9, - -2.05 - ], - "size": [ - 0.35, - 4.1, - 4.5 - ], - "pivot": [ - -8.35, - 15, - -2.15 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 56, - 60 - ], - "uv_size": [ - -1, - 4 - ] - }, - "east": { - "uv": [ - 52, - 60 - ], - "uv_size": [ - 4, - 4 - ] - }, - "south": { - "uv": [ - 53, - 60 - ], - "uv_size": [ - -1, - 4 - ] - }, - "west": { - "uv": [ - 44, - 44 - ], - "uv_size": [ - 4, - 4 - ] - }, - "up": { - "uv": [ - 53, - 53 - ], - "uv_size": [ - 1, - -1 - ] - }, - "down": { - "uv": [ - 53, - 53 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -8.45, - 11.9, - -2.25 - ], - "size": [ - 0.35, - 5.1, - 4.5 - ], - "uv": { - "north": { - "uv": [ - 43, - 43 - ], - "uv_size": [ - 1, - 5 - ] - }, - "east": { - "uv": [ - 40, - 43 - ], - "uv_size": [ - 4, - 5 - ] - }, - "south": { - "uv": [ - 40, - 43 - ], - "uv_size": [ - 1, - 5 - ] - }, - "west": { - "uv": [ - 56, - 59 - ], - "uv_size": [ - 4, - 5 - ] - }, - "up": { - "uv": [ - 56, - 60 - ], - "uv_size": [ - 3, - -1 - ] - }, - "down": { - "uv": [ - 55, - 64 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -3.95, - 11.9, - -2.25 - ], - "size": [ - 0.35, - 4.1, - 4.5 - ], - "uv": { - "north": { - "uv": [ - 49, - 59 - ], - "uv_size": [ - 4, - 5 - ] - }, - "east": { - "uv": [ - 52, - 44 - ], - "uv_size": [ - -4, - 4 - ] - }, - "south": { - "uv": [ - 49, - 59 - ], - "uv_size": [ - 4, - 5 - ] - }, - "west": { - "uv": [ - 48, - 44 - ], - "uv_size": [ - 4, - 4 - ] - }, - "up": { - "uv": [ - 56, - 60 - ], - "uv_size": [ - 3, - -1 - ] - } - } - }, - { - "origin": [ - -7.1, - 24, - -2.3 - ], - "size": [ - 2.2, - 0.2, - 4.5 - ], - "uv": { - "north": { - "uv": [ - 45, - 35 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 54, - 49 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 45, - 32 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 47, - 32 - ], - "uv_size": [ - -2, - 4 - ] - }, - "down": { - "uv": [ - 47, - 36 - ], - "uv_size": [ - -2, - -4 - ] - } - } - }, - { - "origin": [ - -8.2, - 11.9, - -2.2 - ], - "size": [ - 4.4, - 0.2, - 4.4 - ], - "uv": { - "north": { - "uv": [ - 55, - 48 - ], - "uv_size": [ - -2, - 1 - ] - }, - "south": { - "uv": [ - 53, - 48 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 52, - 36 - ], - "uv_size": [ - -4, - -4 - ] - }, - "down": { - "uv": [ - 52, - 36 - ], - "uv_size": [ - -4, - -4 - ] - } - } - }, - { - "origin": [ - -8.2, - 24, - -1.3 - ], - "size": [ - 1.1, - 0.2, - 2.5 - ], - "uv": { - "north": { - "uv": [ - 55, - 49 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 55, - 49 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 55, - 49 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 45, - 35 - ], - "uv_size": [ - -1, - -2 - ] - }, - "down": { - "uv": [ - 45, - 35 - ], - "uv_size": [ - -1, - -2 - ] - } - } - } - ] - }, - { - "name": "bipedLeftArm", - "pivot": [ - 4, - 22, - 0 - ] - }, - { - "name": "armorLeftArm", - "parent": "bipedLeftArm", - "pivot": [ - 4, - 22, - 0 - ] - }, - { - "name": "bone8", - "parent": "armorLeftArm", - "pivot": [ - -4, - 22, - 0 - ], - "cubes": [ - { - "origin": [ - 3.9, - 11.9, - -2.1 - ], - "size": [ - 4.2, - 12.2, - 4.2 - ], - "uv": { - "north": { - "uv": [ - 36, - 52 - ], - "uv_size": [ - 4, - 12 - ] - }, - "east": { - "uv": [ - 32, - 52 - ], - "uv_size": [ - 4, - 12 - ] - }, - "south": { - "uv": [ - 44, - 52 - ], - "uv_size": [ - 4, - 12 - ] - }, - "west": { - "uv": [ - 40, - 52 - ], - "uv_size": [ - 4, - 12 - ] - }, - "up": { - "uv": [ - 36, - 48 - ], - "uv_size": [ - 4, - 4 - ] - } - } - } - ] - }, - { - "name": "armorLeftArmOut", - "parent": "bone8", - "pivot": [ - 7.1, - 24.1, - 2 - ], - "cubes": [ - { - "origin": [ - 8.1, - 18, - -2.25 - ], - "size": [ - 0.35, - 5, - 4.5 - ], - "uv": { - "north": { - "uv": [ - 56, - 53 - ], - "uv_size": [ - 1, - 5 - ] - }, - "east": { - "uv": [ - 60, - 53 - ], - "uv_size": [ - -4, - 5 - ] - }, - "south": { - "uv": [ - 59, - 53 - ], - "uv_size": [ - 1, - 5 - ] - }, - "west": { - "uv": [ - 56, - 53 - ], - "uv_size": [ - 4, - 5 - ] - }, - "up": { - "uv": [ - 57, - 57 - ], - "uv_size": [ - -1, - -4 - ] - }, - "down": { - "uv": [ - 60, - 57 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - 8.1, - 17, - -1.25 - ], - "size": [ - 0.35, - 1, - 3.5 - ], - "uv": { - "north": { - "uv": [ - 58, - 58 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 59, - 58 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 57, - 58 - ], - "uv_size": [ - 3, - 1 - ] - } - } - }, - { - "origin": [ - 8.1, - 23, - -1.15 - ], - "size": [ - 0.35, - 1, - 2.3 - ], - "uv": { - "north": { - "uv": [ - 57, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 57, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 57, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 59, - 53 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 6.9, - 23, - 2.55 - ], - "size": [ - 0.35, - 1, - 2.3 - ], - "pivot": [ - 7.25, - 23, - 2.45 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 61, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 61, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 63, - 53 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 63, - 53 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 8.1, - 20.95, - 2.55 - ], - "size": [ - 0.25, - 2.05, - 1.2 - ], - "pivot": [ - 8.35, - 22, - 2.45 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 61, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 61, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 63, - 53 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 8, - 17.9, - 2.55 - ], - "size": [ - 0.35, - 1.1, - 1.2 - ], - "pivot": [ - 8.35, - 18, - 2.45 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 61, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 61, - 52 - ], - "uv_size": [ - 2, - 1 - ] - } - } - }, - { - "origin": [ - 4.7, - 17.9, - 2.55 - ], - "size": [ - 0.35, - 1.1, - 1.2 - ], - "pivot": [ - 5.05, - 18, - 2.45 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 63, - 59 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 63, - 59 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 64, - 60 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 64, - 60 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.7, - 19, - 2.65 - ], - "size": [ - 0.35, - 1, - 1.1 - ], - "pivot": [ - 5.05, - 19, - 2.45 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 63, - 59 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 63, - 59 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 64, - 60 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 8, - 19, - 2.55 - ], - "size": [ - 0.35, - 2, - 3.4 - ], - "pivot": [ - 8.35, - 19, - 2.45 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 60, - 55 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 60, - 55 - ], - "uv_size": [ - 3, - 2 - ] - }, - "up": { - "uv": [ - 64, - 60 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 63, - 56 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 8, - 16, - 2.55 - ], - "size": [ - 0.35, - 2, - 2.3 - ], - "pivot": [ - 8.35, - 17, - 2.45 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 61, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 61, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 63, - 53 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 63, - 53 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 7.9, - 11.9, - 2.55 - ], - "size": [ - 0.35, - 4.1, - 4.3 - ], - "pivot": [ - 8.25, - 11.9, - 2.45 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 60, - 60 - ], - "uv_size": [ - 1, - 4 - ] - }, - "west": { - "uv": [ - 60, - 60 - ], - "uv_size": [ - 4, - 4 - ] - }, - "up": { - "uv": [ - 60, - 60 - ], - "uv_size": [ - 1, - 4 - ] - }, - "down": { - "uv": [ - 63, - 53 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 7, - 23, - -2.05 - ], - "size": [ - 0.35, - 1, - 2.3 - ], - "pivot": [ - 7.25, - 23, - -2.15 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 54, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 53, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 53, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 53, - 52 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 54, - 53 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 54, - 53 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 8.1, - 16, - -2.05 - ], - "size": [ - 0.35, - 1, - 2.3 - ], - "pivot": [ - 8.35, - 16, - -2.15 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 54, - 59 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 54, - 59 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 54, - 59 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 54, - 59 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 56, - 60 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 4.9, - 16, - -1.95 - ], - "size": [ - 0.35, - 1, - 1.2 - ], - "pivot": [ - 5.15, - 16, - -2.15 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 54, - 59 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 54, - 59 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 54, - 59 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 54, - 59 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 56, - 60 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 56, - 60 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 8.1, - 21, - -2.05 - ], - "size": [ - 0.35, - 2, - 4.5 - ], - "pivot": [ - 8.35, - 22, - -2.15 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 54, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 52, - 53 - ], - "uv_size": [ - 4, - 2 - ] - }, - "south": { - "uv": [ - 53, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 56, - 53 - ], - "uv_size": [ - -4, - 2 - ] - }, - "up": { - "uv": [ - 54, - 53 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 54, - 53 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 8.1, - 20, - -2.05 - ], - "size": [ - 0.35, - 1, - 3.4 - ], - "pivot": [ - 8.35, - 21, - -2.15 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 54, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 53, - 55 - ], - "uv_size": [ - 3, - 1 - ] - }, - "south": { - "uv": [ - 53, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 56, - 53 - ], - "uv_size": [ - -4, - 2 - ] - } - } - }, - { - "origin": [ - 8.1, - 17.9, - -2.05 - ], - "size": [ - 0.35, - 2.1, - 4.5 - ], - "pivot": [ - 8.35, - 19, - -2.15 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 54, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 52, - 56 - ], - "uv_size": [ - 4, - 2 - ] - }, - "south": { - "uv": [ - 53, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 56, - 56 - ], - "uv_size": [ - -4, - 2 - ] - }, - "up": { - "uv": [ - 54, - 53 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 54, - 53 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 8.1, - 11.9, - -2.05 - ], - "size": [ - 0.35, - 4.1, - 4.5 - ], - "pivot": [ - 8.35, - 15, - -2.15 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 55, - 60 - ], - "uv_size": [ - 1, - 4 - ] - }, - "east": { - "uv": [ - 52, - 60 - ], - "uv_size": [ - 4, - 4 - ] - }, - "south": { - "uv": [ - 52, - 60 - ], - "uv_size": [ - 1, - 4 - ] - }, - "west": { - "uv": [ - 56, - 60 - ], - "uv_size": [ - -4, - 4 - ] - }, - "up": { - "uv": [ - 54, - 53 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 54, - 53 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 8.1, - 11.9, - -2.25 - ], - "size": [ - 0.35, - 5.1, - 4.5 - ], - "uv": { - "north": { - "uv": [ - 56, - 59 - ], - "uv_size": [ - 1, - 5 - ] - }, - "east": { - "uv": [ - 60, - 59 - ], - "uv_size": [ - -4, - 5 - ] - }, - "south": { - "uv": [ - 59, - 59 - ], - "uv_size": [ - 1, - 5 - ] - }, - "west": { - "uv": [ - 56, - 59 - ], - "uv_size": [ - 4, - 5 - ] - }, - "up": { - "uv": [ - 59, - 60 - ], - "uv_size": [ - -3, - -1 - ] - }, - "down": { - "uv": [ - 56, - 64 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 3.6, - 11.9, - -2.25 - ], - "size": [ - 0.35, - 5.1, - 4.5 - ], - "uv": { - "north": { - "uv": [ - 53, - 59 - ], - "uv_size": [ - -4, - 5 - ] - }, - "east": { - "uv": [ - 53, - 59 - ], - "uv_size": [ - -4, - 5 - ] - }, - "south": { - "uv": [ - 53, - 59 - ], - "uv_size": [ - -4, - 5 - ] - }, - "west": { - "uv": [ - 53, - 59 - ], - "uv_size": [ - -4, - 5 - ] - }, - "up": { - "uv": [ - 59, - 60 - ], - "uv_size": [ - -3, - -1 - ] - }, - "down": { - "uv": [ - 56, - 64 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 3.6, - 17.9, - -2.25 - ], - "size": [ - 0.35, - 2.1, - 4.5 - ], - "uv": { - "east": { - "uv": [ - 53, - 56 - ], - "uv_size": [ - -4, - 2 - ] - }, - "south": { - "uv": [ - 53, - 56 - ], - "uv_size": [ - -4, - 2 - ] - }, - "west": { - "uv": [ - 53, - 56 - ], - "uv_size": [ - -4, - 2 - ] - }, - "up": { - "uv": [ - 49, - 58 - ], - "uv_size": [ - 4, - -2 - ] - }, - "down": { - "uv": [ - 49, - 58 - ], - "uv_size": [ - 4, - -2 - ] - } - } - }, - { - "origin": [ - 4.9, - 24, - -2.3 - ], - "size": [ - 2.2, - 0.2, - 4.5 - ], - "uv": { - "north": { - "uv": [ - 53, - 48 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 55, - 48 - ], - "uv_size": [ - -2, - 1 - ] - }, - "west": { - "uv": [ - 55, - 49 - ], - "uv_size": [ - -1, - 2 - ] - }, - "up": { - "uv": [ - 53, - 52 - ], - "uv_size": [ - 2, - -4 - ] - }, - "down": { - "uv": [ - 53, - 52 - ], - "uv_size": [ - 2, - -4 - ] - } - } - }, - { - "origin": [ - 3.8, - 11.9, - -2.2 - ], - "size": [ - 4.4, - 0.2, - 4.4 - ], - "uv": { - "north": { - "uv": [ - 53, - 48 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 55, - 48 - ], - "uv_size": [ - -2, - 1 - ] - }, - "up": { - "uv": [ - 53, - 64 - ], - "uv_size": [ - 2, - -4 - ] - }, - "down": { - "uv": [ - 56, - 52 - ], - "uv_size": [ - 4, - -4 - ] - } - } - }, - { - "origin": [ - 7.1, - 24, - -1.3 - ], - "size": [ - 1.1, - 0.2, - 2.5 - ], - "uv": { - "north": { - "uv": [ - 56, - 49 - ], - "uv_size": [ - -1, - 2 - ] - }, - "south": { - "uv": [ - 56, - 49 - ], - "uv_size": [ - -1, - 2 - ] - }, - "west": { - "uv": [ - 56, - 49 - ], - "uv_size": [ - -1, - 2 - ] - }, - "up": { - "uv": [ - 55, - 51 - ], - "uv_size": [ - 1, - -2 - ] - }, - "down": { - "uv": [ - 55, - 51 - ], - "uv_size": [ - 1, - -2 - ] - } - } - } - ] - }, - { - "name": "launcher", - "parent": "bone8", - "pivot": [ - 6, - 23.75, - -0.75 - ], - "cubes": [ - { - "origin": [ - 6.5, - 22.95, - 0.5 - ], - "size": [ - 1, - 3.5, - 1 - ], - "pivot": [ - 7.5, - 22.95, - 0.25 - ], - "rotation": [ - -47.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6, - 24.2, - 0.25 - ], - "size": [ - 2, - 1, - 1.5 - ], - "pivot": [ - 7.5, - 22.95, - 0.25 - ], - "rotation": [ - -47.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6.5, - 24.8, - 3.35 - ], - "size": [ - 1, - 1.25, - 1.5 - ], - "inflate": 0.01, - "pivot": [ - 7.5, - 25.05, - 3.35 - ], - "rotation": [ - 55, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6.5, - 24.35, - 2.7 - ], - "size": [ - 1, - 1.25, - 1 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6.25, - 25.6, - 2.2 - ], - "size": [ - 1.5, - 1.25, - 1.75 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - } - ] - }, - { - "name": "bone4", - "parent": "launcher", - "pivot": [ - 7, - 27.25, - 2.7 - ], - "cubes": [ - { - "origin": [ - 7.5, - 26.45, - -1.3 - ], - "size": [ - 0.5, - 1.6, - 5 - ], - "inflate": 0.01, - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 7.5, - 27.9, - -1.05 - ], - "size": [ - 0.5, - 0.6, - 3.75 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6, - 27.9, - -1.05 - ], - "size": [ - 0.5, - 0.6, - 3.75 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 7.75, - 27.2, - -0.3 - ], - "size": [ - 0.5, - 0.85, - 3 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 5.75, - 27.2, - -0.3 - ], - "size": [ - 0.5, - 0.85, - 3 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 7.75, - 26.35, - -0.8 - ], - "size": [ - 0.5, - 0.85, - 3.5 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 5.75, - 26.35, - -0.8 - ], - "size": [ - 0.5, - 0.85, - 3.5 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 7.5, - 26.1, - -1.05 - ], - "size": [ - 0.5, - 0.35, - 3.75 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6.5, - 25.85, - -1.05 - ], - "size": [ - 1, - 0.35, - 3.75 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6.5, - 28.35, - -1.05 - ], - "size": [ - 1, - 0.35, - 3.75 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6.5, - 28.1, - 1.45 - ], - "size": [ - 1, - 0.6, - 1.75 - ], - "inflate": 0.1, - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6, - 26.1, - -1.05 - ], - "size": [ - 0.5, - 0.35, - 3.75 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6, - 26.45, - -1.3 - ], - "size": [ - 0.5, - 1.6, - 5 - ], - "inflate": 0.01, - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6.5, - 26.1, - -1.3 - ], - "size": [ - 1, - 0.65, - 5 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6.5, - 27.85, - -1.3 - ], - "size": [ - 1, - 0.65, - 5 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 6.5, - 26.6, - 3.7 - ], - "size": [ - 1, - 1.4, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 59, - 13 - ], - "uv_size": [ - -1, - -1 - ] - } - } - } - ] - }, - { - "name": "doomblade", - "parent": "bone8", - "pivot": [ - 8.2, - 14.8, - 0 - ], - "rotation": [ - 90, - 180, - 90 - ] - }, - { - "name": "group4", - "parent": "doomblade", - "pivot": [ - -16.2, - 16.65, - 16.5 - ], - "cubes": [ - { - "origin": [ - 8.95, - 14.05, - 0.375 - ], - "size": [ - 0.75, - 1.5, - 1.125 - ], - "inflate": 0.01, - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 9.7, - 14.05, - 0.375 - ], - "size": [ - 0.75, - 1.5, - 0.75 - ], - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 5.3, - 14.05, - 0.375 - ], - "size": [ - 0.45, - 1.5, - 0.875 - ], - "inflate": 0.01, - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.33, - 14.3, - 0.375 - ], - "size": [ - 0.95, - 1, - 0.375 - ], - "inflate": 0.01, - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 2.83, - 14.3, - -0.02 - ], - "size": [ - 2.45, - 1, - 0.375 - ], - "inflate": 0.01, - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 3.08, - 14.55, - 0.375 - ], - "size": [ - 2.2, - 0.5, - 0.375 - ], - "inflate": 0.01, - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 5.55, - 14.05, - 0.875 - ], - "size": [ - 0.7, - 1.5, - 0.625 - ], - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 5.55, - 14.05, - 0.5 - ], - "size": [ - 0.45, - 1.5, - 0.375 - ], - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 8.7, - 14.05, - 1 - ], - "size": [ - 0.45, - 1.5, - 0.375 - ], - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 8.55, - 14.05, - 0.375 - ], - "size": [ - 0.7, - 1.5, - 0.625 - ], - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 5.05, - 14.05, - 0 - ], - "size": [ - 5.25, - 1.5, - 0.375 - ], - "uv": { - "north": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 11, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 12, - 4 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 5.7, - 14.2, - 0.375 - ], - "size": [ - 3.25, - 1.2, - 0.9 - ], - "uv": { - "north": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 3, - 1 - ] - }, - "east": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 3, - 1 - ] - }, - "south": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 3, - 1 - ] - }, - "west": { - "uv": [ - 58, - 12 - ], - "uv_size": [ - 3, - 1 - ] - }, - "up": { - "uv": [ - 61, - 13 - ], - "uv_size": [ - -3, - -1 - ] - }, - "down": { - "uv": [ - 61, - 13 - ], - "uv_size": [ - -3, - -1 - ] - } - } - } - ] - }, - { - "name": "group5", - "parent": "doomblade", - "pivot": [ - -16.2, - 16.65, - 16.5 - ], - "cubes": [ - { - "origin": [ - 4, - 15.565, - -6.375 - ], - "size": [ - 0.15, - 0.015, - 0.375 - ], - "pivot": [ - -10.1, - 9.55, - 6.75 - ], - "rotation": [ - 0, - -22.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.225, - 15.565, - -6.375 - ], - "size": [ - 0.15, - 0.015, - 0.375 - ], - "pivot": [ - -9.875, - 9.55, - 6.75 - ], - "rotation": [ - 0, - -22.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.45, - 15.565, - -6.375 - ], - "size": [ - 0.15, - 0.015, - 0.375 - ], - "pivot": [ - -9.65, - 9.55, - 6.75 - ], - "rotation": [ - 0, - -22.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.675, - 15.565, - -6.375 - ], - "size": [ - 0.15, - 0.015, - 0.375 - ], - "pivot": [ - -9.425, - 9.55, - 6.75 - ], - "rotation": [ - 0, - -22.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - } - ] - }, - { - "name": "group6", - "parent": "doomblade", - "pivot": [ - -16.2, - 16.65, - 16.5 - ], - "cubes": [ - { - "origin": [ - 4, - 14.035, - -6.375 - ], - "size": [ - 0.15, - 0.015, - 0.375 - ], - "pivot": [ - -10.1, - 9.55, - 6.75 - ], - "rotation": [ - 0, - -22.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.225, - 14.035, - -6.375 - ], - "size": [ - 0.15, - 0.015, - 0.375 - ], - "pivot": [ - -9.875, - 9.55, - 6.75 - ], - "rotation": [ - 0, - -22.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.45, - 14.035, - -6.375 - ], - "size": [ - 0.15, - 0.015, - 0.375 - ], - "pivot": [ - -9.65, - 9.55, - 6.75 - ], - "rotation": [ - 0, - -22.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.675, - 14.035, - -6.375 - ], - "size": [ - 0.15, - 0.015, - 0.375 - ], - "pivot": [ - -9.425, - 9.55, - 6.75 - ], - "rotation": [ - 0, - -22.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 2, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 3, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - } - ] - }, - { - "name": "blade", - "parent": "doomblade", - "pivot": [ - 4.8, - 14.85, - 0.7 - ], - "cubes": [ - { - "origin": [ - 0.36392, - 14.7, - 0.45238 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 0.4223, - 14.55, - 0.72738 - ], - "rotation": [ - 0, - -27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 0.26662, - 14.7, - 0.9 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 0.26662, - 14.7, - 0.1 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 0.75396, - 14.7, - 0.69158 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - -0.18766, - 14.55, - 1.51658 - ], - "rotation": [ - 0, - 27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "up": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - }, - "down": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - } - } - }, - { - "origin": [ - 1.11392, - 14.7, - 0.45238 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 1.1723, - 14.55, - 0.72738 - ], - "rotation": [ - 0, - -27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 1.01662, - 14.7, - 0.9 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 1.05, - 14.75, - 1.15 - ], - "size": [ - 0.25, - 0.1, - 0.225 - ], - "pivot": [ - 1.25, - 14.75, - 1.25 - ], - "rotation": [ - 0, - -62.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 1.01662, - 14.7, - 0.1 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 1.50396, - 14.7, - 0.69158 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 0.56234, - 14.55, - 1.51658 - ], - "rotation": [ - 0, - 27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "up": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - }, - "down": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - } - } - }, - { - "origin": [ - 2.25396, - 14.7, - 0.69158 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 1.31234, - 14.55, - 1.51658 - ], - "rotation": [ - 0, - 27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "up": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - }, - "down": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - } - } - }, - { - "origin": [ - 1.76662, - 14.7, - 0.1 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 1.8, - 14.75, - 1.15 - ], - "size": [ - 0.25, - 0.1, - 0.225 - ], - "pivot": [ - 2, - 14.75, - 1.25 - ], - "rotation": [ - 0, - -62.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "up": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - }, - "down": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - } - } - }, - { - "origin": [ - 1.76662, - 14.7, - 0.9 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 1.86392, - 14.7, - 0.45238 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 1.9223, - 14.55, - 0.72738 - ], - "rotation": [ - 0, - -27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 3.00396, - 14.7, - 0.69158 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 2.06234, - 14.55, - 1.51658 - ], - "rotation": [ - 0, - 27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "up": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - }, - "down": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - } - } - }, - { - "origin": [ - 2.51662, - 14.7, - 0.1 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 2.55, - 14.75, - 1.15 - ], - "size": [ - 0.25, - 0.1, - 0.225 - ], - "pivot": [ - 2.75, - 14.75, - 1.25 - ], - "rotation": [ - 0, - -62.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "up": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - }, - "down": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - } - } - }, - { - "origin": [ - 2.51662, - 14.7, - 0.9 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 2.61392, - 14.7, - 0.45238 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 2.6723, - 14.55, - 0.72738 - ], - "rotation": [ - 0, - -27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 3.75396, - 14.7, - 0.69158 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 2.81234, - 14.55, - 1.51658 - ], - "rotation": [ - 0, - 27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "up": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - }, - "down": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - } - } - }, - { - "origin": [ - 3.26662, - 14.7, - 0.1 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 3.3, - 14.75, - 1.15 - ], - "size": [ - 0.25, - 0.1, - 0.225 - ], - "pivot": [ - 3.5, - 14.75, - 1.25 - ], - "rotation": [ - 0, - -62.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "up": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - }, - "down": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - } - } - }, - { - "origin": [ - 3.26662, - 14.7, - 0.9 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 3.36392, - 14.7, - 0.45238 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 3.4223, - 14.55, - 0.72738 - ], - "rotation": [ - 0, - -27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.50396, - 14.7, - 0.69158 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 3.56234, - 14.55, - 1.51658 - ], - "rotation": [ - 0, - 27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "up": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - }, - "down": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - } - } - }, - { - "origin": [ - 4.01662, - 14.7, - 0.1 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.05, - 14.75, - 1.15 - ], - "size": [ - 0.25, - 0.1, - 0.225 - ], - "pivot": [ - 4.25, - 14.75, - 1.25 - ], - "rotation": [ - 0, - -62.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "up": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - }, - "down": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - } - } - }, - { - "origin": [ - 4.01662, - 14.7, - 0.9 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.11392, - 14.7, - 0.45238 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 4.1723, - 14.55, - 0.72738 - ], - "rotation": [ - 0, - -27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 5.25396, - 14.7, - 0.69158 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 4.31234, - 14.55, - 1.51658 - ], - "rotation": [ - 0, - 27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.375, - 0.2 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 0.5, - 0.2 - ] - }, - "up": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - }, - "down": { - "uv": [ - 5.375, - 5.5 - ], - "uv_size": [ - -0.375, - -0.5 - ] - } - } - }, - { - "origin": [ - 4.76662, - 14.7, - 0.1 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.8, - 14.75, - 1.15 - ], - "size": [ - 0.25, - 0.1, - 0.225 - ], - "pivot": [ - 5, - 14.75, - 1.25 - ], - "rotation": [ - 0, - -62.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.25, - 0.1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 0.225, - 0.1 - ] - }, - "up": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - }, - "down": { - "uv": [ - 0.25, - 5.225 - ], - "uv_size": [ - -0.25, - -0.225 - ] - } - } - }, - { - "origin": [ - 4.76662, - 14.7, - 0.9 - ], - "size": [ - 0.375, - 0.2, - 0.25 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 4.86392, - 14.7, - 0.45238 - ], - "size": [ - 0.375, - 0.2, - 0.5 - ], - "pivot": [ - 4.9223, - 14.55, - 0.72738 - ], - "rotation": [ - 0, - -27.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 0.55, - 14.75, - 0.15 - ], - "size": [ - 4.75, - 0.1, - 0.975 - ], - "uv": { - "north": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 0, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 1, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -1.2, - 14.75, - 0.15 - ], - "size": [ - 1.75, - 0.1, - 0.725 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -2.00595, - 14.7, - 0.0951 - ], - "size": [ - 2.5, - 0.2, - 0.225 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -1.50595, - 14.7, - 0.3201 - ], - "size": [ - 2, - 0.2, - 0.225 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -0.23338, - 14.7, - 0.675 - ], - "size": [ - 0.75, - 0.2, - 0.475 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -1.00595, - 14.7, - 0.5451 - ], - "size": [ - 1.75, - 0.2, - 0.225 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -1.55597, - 14.7, - 1.07007 - ], - "size": [ - 1, - 0.2, - 0.475 - ], - "pivot": [ - 0.39403, - 0, - 0.42007 - ], - "rotation": [ - 0, - -25, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -1.73967, - 14.7, - 1.57457 - ], - "size": [ - 0.25, - 0.2, - 0.475 - ], - "pivot": [ - -0.03967, - 0, - 0.92457 - ], - "rotation": [ - 0, - -47.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -1.92377, - 14.7, - 1.71887 - ], - "size": [ - 0.25, - 0.2, - 0.225 - ], - "pivot": [ - -0.22377, - 0, - 0.81887 - ], - "rotation": [ - 0, - -50, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -0.88962, - 14.7, - 0.5842 - ], - "size": [ - 0.75, - 0.2, - 0.475 - ], - "pivot": [ - -0.79584, - 15, - 0.74701 - ], - "rotation": [ - 0, - -10, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - -0.35269, - 14.7, - 0.66088 - ], - "size": [ - 0.5, - 0.2, - 0.475 - ], - "pivot": [ - -0.25891, - 15, - 0.82369 - ], - "rotation": [ - 0, - -17.5, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 5, - 5 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 6, - 6 - ], - "uv_size": [ - -1, - -1 - ] - } - } - } - ] - }, - { - "name": "bipedRightLeg", - "pivot": [ - -2, - 12, - 0 - ] - }, - { - "name": "armorRightLeg", - "parent": "bipedRightLeg", - "pivot": [ - -2, - 12, - 0 - ] - }, - { - "name": "bone6", - "parent": "armorRightLeg", - "pivot": [ - -4, - 22, - 0 - ], - "cubes": [ - { - "origin": [ - -0.1, - 0.2, - -2.2 - ], - "size": [ - 4.4, - 11.8, - 4.4 - ], - "uv": { - "north": { - "uv": [ - 8, - 20 - ], - "uv_size": [ - -4, - 12 - ] - }, - "south": { - "uv": [ - 16, - 20 - ], - "uv_size": [ - -4, - 12 - ] - }, - "west": { - "uv": [ - 4, - 20 - ], - "uv_size": [ - -4, - 12 - ] - }, - "up": { - "uv": [ - 8, - 20 - ], - "uv_size": [ - -4, - -4 - ] - } - } - } - ] - }, - { - "name": "armorRightBoot", - "parent": "bipedRightLeg", - "pivot": [ - -2, - 12, - 0 - ] - }, - { - "name": "bone9", - "parent": "armorRightBoot", - "pivot": [ - -4, - 22, - 0 - ] - }, - { - "name": "bone14", - "parent": "bone9", - "pivot": [ - -4, - 22, - 0 - ], - "cubes": [ - { - "origin": [ - 0, - -0.3, - -2.3 - ], - "size": [ - 4.6, - 2.2, - 4.6 - ], - "uv": { - "north": { - "uv": [ - 4, - 52 - ], - "uv_size": [ - 4, - 12 - ] - }, - "south": { - "uv": [ - 12, - 52 - ], - "uv_size": [ - 4, - 12 - ] - }, - "west": { - "uv": [ - 8, - 52 - ], - "uv_size": [ - 4, - 12 - ] - }, - "up": { - "uv": [ - 12, - 20 - ], - "uv_size": [ - -4, - -4 - ] - }, - "down": { - "uv": [ - 12, - 20 - ], - "uv_size": [ - -4, - -4 - ] - } - } - }, - { - "origin": [ - 0, - 2.8, - -2.5 - ], - "size": [ - 4.6, - 4.1, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 4, - 57 - ], - "uv_size": [ - 4, - 4 - ] - }, - "south": { - "uv": [ - 4, - 57 - ], - "uv_size": [ - 4, - 4 - ] - }, - "west": { - "uv": [ - 5, - 57 - ], - "uv_size": [ - -1, - 4 - ] - }, - "up": { - "uv": [ - 8, - 58 - ], - "uv_size": [ - -4, - -1 - ] - }, - "down": { - "uv": [ - 4, - 61 - ], - "uv_size": [ - 4, - -1 - ] - } - } - }, - { - "origin": [ - 0, - 5.8, - 2.2 - ], - "size": [ - 4.6, - 1.1, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 12, - 57 - ], - "uv_size": [ - 4, - 1 - ] - }, - "south": { - "uv": [ - 12, - 57 - ], - "uv_size": [ - 4, - 1 - ] - }, - "west": { - "uv": [ - 12, - 57 - ], - "uv_size": [ - 4, - 1 - ] - }, - "up": { - "uv": [ - 16, - 58 - ], - "uv_size": [ - -4, - -1 - ] - }, - "down": { - "uv": [ - 16, - 58 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - 0, - 2.8, - 2 - ], - "size": [ - 4.6, - 1.1, - 0.5 - ], - "uv": { - "north": { - "uv": [ - 12, - 60 - ], - "uv_size": [ - 4, - 1 - ] - }, - "south": { - "uv": [ - 12, - 60 - ], - "uv_size": [ - 4, - 1 - ] - }, - "west": { - "uv": [ - 12, - 60 - ], - "uv_size": [ - 4, - 1 - ] - }, - "up": { - "uv": [ - 16, - 61 - ], - "uv_size": [ - -4, - -1 - ] - }, - "down": { - "uv": [ - 16, - 61 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - 0, - -0.2, - -2.5 - ], - "size": [ - 4.6, - 2.1, - 0.7 - ], - "uv": { - "north": { - "uv": [ - 4, - 62 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 1, - 62 - ], - "uv_size": [ - 4, - 2 - ] - }, - "up": { - "uv": [ - 8, - 63 - ], - "uv_size": [ - -4, - -1 - ] - }, - "down": { - "uv": [ - 8, - 64 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - 2.3, - 6.9, - -2.5 - ], - "size": [ - 2.3, - 2.1, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 6, - 55 - ], - "uv_size": [ - 2, - 2 - ] - }, - "east": { - "uv": [ - 6, - 55 - ], - "uv_size": [ - 2, - 2 - ] - }, - "south": { - "uv": [ - 6, - 55 - ], - "uv_size": [ - 2, - 2 - ] - }, - "west": { - "uv": [ - 6, - 55 - ], - "uv_size": [ - 2, - 2 - ] - }, - "up": { - "uv": [ - 8, - 57 - ], - "uv_size": [ - -2, - -2 - ] - } - } - }, - { - "origin": [ - 0, - 6.9, - 2.2 - ], - "size": [ - 3.5, - 2.1, - 0.3 - ], - "uv": { - "east": { - "uv": [ - 12, - 55 - ], - "uv_size": [ - 3, - 2 - ] - }, - "south": { - "uv": [ - 12, - 55 - ], - "uv_size": [ - 3, - 2 - ] - }, - "west": { - "uv": [ - 12, - 55 - ], - "uv_size": [ - 3, - 2 - ] - }, - "up": { - "uv": [ - 15, - 57 - ], - "uv_size": [ - -3, - -2 - ] - } - } - }, - { - "origin": [ - 0, - 9, - 2.2 - ], - "size": [ - 1.2, - 1, - 0.3 - ], - "uv": { - "east": { - "uv": [ - 12, - 55 - ], - "uv_size": [ - 3, - 2 - ] - }, - "south": { - "uv": [ - 12, - 55 - ], - "uv_size": [ - 3, - 2 - ] - }, - "west": { - "uv": [ - 12, - 55 - ], - "uv_size": [ - 3, - 2 - ] - }, - "up": { - "uv": [ - 15, - 57 - ], - "uv_size": [ - -3, - -2 - ] - } - } - }, - { - "origin": [ - 0, - 10.9, - -2.5 - ], - "size": [ - 1.2, - 1.1, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 4, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 4, - 52 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 5, - 53 - ], - "uv_size": [ - -1, - -1 - ] - }, - "down": { - "uv": [ - 5, - 53 - ], - "uv_size": [ - -1, - -1 - ] - } - } - }, - { - "origin": [ - 3.3, - 9, - -2.5 - ], - "size": [ - 1.3, - 2, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 7, - 53 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 7, - 53 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 7, - 53 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 7, - 53 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 8, - 55 - ], - "uv_size": [ - -1, - -2 - ] - } - } - }, - { - "origin": [ - 4.6, - -0.2, - 2 - ], - "size": [ - 4.6, - 2.1, - 0.5 - ], - "pivot": [ - 4.6, - -0.2, - 2.3 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "east": { - "uv": [ - 12, - 62 - ], - "uv_size": [ - 4, - 2 - ] - }, - "south": { - "uv": [ - 8, - 62 - ], - "uv_size": [ - 4, - 2 - ] - }, - "up": { - "uv": [ - 8, - 63 - ], - "uv_size": [ - -4, - -1 - ] - }, - "down": { - "uv": [ - 8, - 64 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - 4.6, - 2.8, - 2.2 - ], - "size": [ - 4.6, - 8.2, - 0.3 - ], - "pivot": [ - 4.6, - 2.8, - 2.3 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "north": { - "uv": [ - 12, - 53 - ], - "uv_size": [ - -4, - 8 - ] - }, - "east": { - "uv": [ - 11, - 53 - ], - "uv_size": [ - 1, - 8 - ] - }, - "south": { - "uv": [ - 8, - 53 - ], - "uv_size": [ - 4, - 8 - ] - }, - "up": { - "uv": [ - 12, - 54 - ], - "uv_size": [ - -4, - -1 - ] - }, - "down": { - "uv": [ - 11, - 61 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - -0.2, - 2.8, - 2.3 - ], - "size": [ - 4.6, - 4.1, - 0.3 - ], - "pivot": [ - -0.2, - 2.8, - 2.3 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "up": { - "uv": [ - 4, - 58 - ], - "uv_size": [ - -4, - -1 - ] - }, - "down": { - "uv": [ - 11, - 61 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - -0.2, - -0.2, - 2.3 - ], - "size": [ - 4.6, - 2.1, - 0.4 - ], - "pivot": [ - -0.2, - -0.2, - 2.3 - ], - "rotation": [ - 0, - 90, - 0 - ], - "uv": { - "up": { - "uv": [ - 8, - 63 - ], - "uv_size": [ - -4, - -1 - ] - }, - "down": { - "uv": [ - 8, - 64 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - 0, - -0.2, - 2.1 - ], - "size": [ - 4.6, - 2.1, - 0.4 - ], - "uv": { - "south": { - "uv": [ - 12, - 62 - ], - "uv_size": [ - 4, - 2 - ] - }, - "west": { - "uv": [ - 12, - 62 - ], - "uv_size": [ - 4, - 2 - ] - }, - "up": { - "uv": [ - 8, - 63 - ], - "uv_size": [ - -4, - -1 - ] - }, - "down": { - "uv": [ - 16, - 64 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - 1.1, - 1.9, - 2.2 - ], - "size": [ - 2.4, - 1, - 0.3 - ], - "uv": { - "east": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - } - } - }, - { - "origin": [ - 0, - 3.9, - 2.1 - ], - "size": [ - 1.2, - 1, - 0.4 - ], - "uv": { - "south": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 15, - 62 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 3.4, - 3.9, - 1.9 - ], - "size": [ - 1.2, - 1, - 0.6 - ], - "uv": { - "east": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 15, - 62 - ], - "uv_size": [ - -2, - -1 - ] - }, - "down": { - "uv": [ - 15, - 62 - ], - "uv_size": [ - -2, - -1 - ] - } - } - }, - { - "origin": [ - 1.1, - 1.9, - -2.5 - ], - "size": [ - 2.4, - 1, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 13, - 61 - ], - "uv_size": [ - 2, - 1 - ] - } - } - } - ] - }, - { - "name": "bipedLeftLeg", - "pivot": [ - 2, - 12, - 0 - ] - }, - { - "name": "armorLeftLeg", - "parent": "bipedLeftLeg", - "pivot": [ - 2, - 12, - 0 - ] - }, - { - "name": "bone7", - "parent": "armorLeftLeg", - "pivot": [ - -4, - 22, - 0 - ] - }, - { - "name": "armorLeftBoot", - "parent": "bipedLeftLeg", - "pivot": [ - 2, - 12, - 0 - ] - }, - { - "name": "bone10", - "parent": "armorLeftBoot", - "pivot": [ - -4, - 22, - 0 - ], - "cubes": [ - { - "origin": [ - -4.2, - -0.2, - -2.3 - ], - "size": [ - 4.6, - 12.2, - 4.6 - ], - "uv": { - "north": { - "uv": [ - 4, - 20 - ], - "uv_size": [ - 4, - 12 - ] - }, - "east": { - "uv": [ - 0, - 20 - ], - "uv_size": [ - 4, - 12 - ] - }, - "south": { - "uv": [ - 12, - 20 - ], - "uv_size": [ - 4, - 12 - ] - }, - "west": { - "uv": [ - 4, - 52 - ], - "uv_size": [ - -4, - 12 - ] - }, - "up": { - "uv": [ - 4, - 0 - ], - "uv_size": [ - 4, - 4 - ] - } - } - }, - { - "origin": [ - -4.2, - -0.3, - -2.3 - ], - "size": [ - 4.6, - 2.2, - 4.6 - ], - "uv": { - "north": { - "uv": [ - 8, - 52 - ], - "uv_size": [ - -4, - 12 - ] - }, - "east": { - "uv": [ - 12, - 52 - ], - "uv_size": [ - -4, - 12 - ] - }, - "south": { - "uv": [ - 16, - 52 - ], - "uv_size": [ - -4, - 12 - ] - }, - "down": { - "uv": [ - 8, - 20 - ], - "uv_size": [ - 4, - -4 - ] - } - } - }, - { - "origin": [ - -4.2, - 2.8, - -2.5 - ], - "size": [ - 4.6, - 4.1, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 8, - 57 - ], - "uv_size": [ - -4, - 4 - ] - }, - "east": { - "uv": [ - 4, - 57 - ], - "uv_size": [ - 1, - 4 - ] - }, - "up": { - "uv": [ - 4, - 58 - ], - "uv_size": [ - 4, - -1 - ] - }, - "down": { - "uv": [ - 8, - 61 - ], - "uv_size": [ - -4, - -1 - ] - } - } - }, - { - "origin": [ - -4.2, - 5.8, - 2.2 - ], - "size": [ - 4.6, - 1.1, - 0.3 - ], - "uv": { - "east": { - "uv": [ - 16, - 57 - ], - "uv_size": [ - -4, - 1 - ] - }, - "south": { - "uv": [ - 16, - 57 - ], - "uv_size": [ - -4, - 1 - ] - }, - "up": { - "uv": [ - 12, - 58 - ], - "uv_size": [ - 4, - -1 - ] - }, - "down": { - "uv": [ - 12, - 58 - ], - "uv_size": [ - 4, - -1 - ] - } - } - }, - { - "origin": [ - -4.2, - 2.8, - 2 - ], - "size": [ - 4.6, - 1.1, - 0.5 - ], - "uv": { - "east": { - "uv": [ - 16, - 60 - ], - "uv_size": [ - -4, - 1 - ] - }, - "south": { - "uv": [ - 16, - 60 - ], - "uv_size": [ - -4, - 1 - ] - }, - "up": { - "uv": [ - 12, - 61 - ], - "uv_size": [ - 4, - -1 - ] - }, - "down": { - "uv": [ - 12, - 61 - ], - "uv_size": [ - 4, - -1 - ] - } - } - }, - { - "origin": [ - -4.2, - -0.2, - -2.5 - ], - "size": [ - 4.6, - 2.1, - 0.7 - ], - "uv": { - "north": { - "uv": [ - 8, - 62 - ], - "uv_size": [ - -4, - 2 - ] - }, - "east": { - "uv": [ - 5, - 62 - ], - "uv_size": [ - -4, - 2 - ] - }, - "up": { - "uv": [ - 4, - 63 - ], - "uv_size": [ - 4, - -1 - ] - }, - "down": { - "uv": [ - 4, - 64 - ], - "uv_size": [ - 4, - -1 - ] - } - } - }, - { - "origin": [ - -4.2, - 6.9, - -2.5 - ], - "size": [ - 2.3, - 2.1, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 8, - 55 - ], - "uv_size": [ - -2, - 2 - ] - }, - "east": { - "uv": [ - 8, - 55 - ], - "uv_size": [ - -2, - 2 - ] - }, - "west": { - "uv": [ - 8, - 55 - ], - "uv_size": [ - -2, - 2 - ] - }, - "up": { - "uv": [ - 6, - 57 - ], - "uv_size": [ - 2, - -2 - ] - } - } - }, - { - "origin": [ - -4.2, - 6.9, - 2.2 - ], - "size": [ - 3.5, - 2.1, - 0.3 - ], - "uv": { - "east": { - "uv": [ - 15, - 55 - ], - "uv_size": [ - -3, - 2 - ] - }, - "south": { - "uv": [ - 15, - 55 - ], - "uv_size": [ - -3, - 2 - ] - }, - "west": { - "uv": [ - 15, - 55 - ], - "uv_size": [ - -3, - 2 - ] - }, - "up": { - "uv": [ - 12, - 57 - ], - "uv_size": [ - 3, - -2 - ] - } - } - }, - { - "origin": [ - -4.2, - 9, - 2.2 - ], - "size": [ - 1.2, - 1, - 0.3 - ], - "uv": { - "east": { - "uv": [ - 15, - 55 - ], - "uv_size": [ - -3, - 2 - ] - }, - "south": { - "uv": [ - 15, - 55 - ], - "uv_size": [ - -3, - 2 - ] - }, - "west": { - "uv": [ - 15, - 55 - ], - "uv_size": [ - -3, - 2 - ] - }, - "up": { - "uv": [ - 12, - 57 - ], - "uv_size": [ - 3, - -2 - ] - } - } - }, - { - "origin": [ - -0.8, - 10.9, - -2.5 - ], - "size": [ - 1.2, - 1.1, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 5, - 52 - ], - "uv_size": [ - -1, - 1 - ] - }, - "east": { - "uv": [ - 5, - 52 - ], - "uv_size": [ - -1, - 1 - ] - }, - "up": { - "uv": [ - 4, - 53 - ], - "uv_size": [ - 1, - -1 - ] - }, - "down": { - "uv": [ - 4, - 53 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -4.2, - 9, - -2.5 - ], - "size": [ - 1.3, - 2, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 8, - 53 - ], - "uv_size": [ - -1, - 2 - ] - }, - "east": { - "uv": [ - 8, - 53 - ], - "uv_size": [ - -1, - 2 - ] - }, - "west": { - "uv": [ - 8, - 53 - ], - "uv_size": [ - -1, - 2 - ] - }, - "up": { - "uv": [ - 7, - 55 - ], - "uv_size": [ - 1, - -2 - ] - } - } - }, - { - "origin": [ - -8.8, - -0.2, - 2 - ], - "size": [ - 4.6, - 2.1, - 0.5 - ], - "pivot": [ - -4.2, - -0.2, - 2.3 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "south": { - "uv": [ - 12, - 62 - ], - "uv_size": [ - -4, - 2 - ] - }, - "west": { - "uv": [ - 16, - 62 - ], - "uv_size": [ - -4, - 2 - ] - }, - "up": { - "uv": [ - 4, - 63 - ], - "uv_size": [ - 4, - -1 - ] - }, - "down": { - "uv": [ - 4, - 64 - ], - "uv_size": [ - 4, - -1 - ] - } - } - }, - { - "origin": [ - -8.8, - 2.8, - 2.2 - ], - "size": [ - 4.6, - 8.2, - 0.3 - ], - "pivot": [ - -4.2, - 2.8, - 2.3 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 8, - 53 - ], - "uv_size": [ - 4, - 8 - ] - }, - "south": { - "uv": [ - 12, - 53 - ], - "uv_size": [ - -4, - 8 - ] - }, - "west": { - "uv": [ - 12, - 53 - ], - "uv_size": [ - -1, - 8 - ] - }, - "up": { - "uv": [ - 8, - 54 - ], - "uv_size": [ - 4, - -1 - ] - }, - "down": { - "uv": [ - 7, - 61 - ], - "uv_size": [ - 4, - -1 - ] - } - } - }, - { - "origin": [ - -4, - 2.8, - 2.3 - ], - "size": [ - 4.6, - 4.1, - 0.3 - ], - "pivot": [ - 0.6, - 2.8, - 2.3 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "north": { - "uv": [ - 4, - 57 - ], - "uv_size": [ - -4, - 4 - ] - }, - "west": { - "uv": [ - 4, - 57 - ], - "uv_size": [ - -4, - 4 - ] - }, - "up": { - "uv": [ - 0, - 58 - ], - "uv_size": [ - 4, - -1 - ] - }, - "down": { - "uv": [ - 7, - 61 - ], - "uv_size": [ - 4, - -1 - ] - } - } - }, - { - "origin": [ - -4, - -0.2, - 2.3 - ], - "size": [ - 4.6, - 2.1, - 0.4 - ], - "pivot": [ - 0.6, - -0.2, - 2.3 - ], - "rotation": [ - 0, - -90, - 0 - ], - "uv": { - "up": { - "uv": [ - 4, - 63 - ], - "uv_size": [ - 4, - -1 - ] - }, - "down": { - "uv": [ - 4, - 64 - ], - "uv_size": [ - 4, - -1 - ] - } - } - }, - { - "origin": [ - -4.2, - -0.2, - 2.1 - ], - "size": [ - 4.6, - 2.1, - 0.4 - ], - "uv": { - "east": { - "uv": [ - 16, - 62 - ], - "uv_size": [ - -4, - 2 - ] - }, - "south": { - "uv": [ - 16, - 62 - ], - "uv_size": [ - -4, - 2 - ] - }, - "up": { - "uv": [ - 4, - 63 - ], - "uv_size": [ - 4, - -1 - ] - }, - "down": { - "uv": [ - 12, - 64 - ], - "uv_size": [ - 4, - -1 - ] - } - } - }, - { - "origin": [ - -3.1, - 1.9, - 2.2 - ], - "size": [ - 2.4, - 1, - 0.3 - ], - "uv": { - "east": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - }, - "south": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - }, - "west": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - } - } - }, - { - "origin": [ - -0.8, - 3.9, - 2.1 - ], - "size": [ - 1.2, - 1, - 0.4 - ], - "uv": { - "east": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - }, - "south": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - }, - "up": { - "uv": [ - 13, - 62 - ], - "uv_size": [ - 2, - -1 - ] - } - } - }, - { - "origin": [ - -4.2, - 3.9, - 1.9 - ], - "size": [ - 1.2, - 1, - 0.6 - ], - "uv": { - "east": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - }, - "south": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - }, - "west": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - }, - "up": { - "uv": [ - 13, - 62 - ], - "uv_size": [ - 2, - -1 - ] - } - } - }, - { - "origin": [ - -3.1, - 1.9, - -2.5 - ], - "size": [ - 2.4, - 1, - 0.3 - ], - "uv": { - "north": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - }, - "east": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - }, - "west": { - "uv": [ - 15, - 61 - ], - "uv_size": [ - -2, - 1 - ] - } - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/geo/item/pistol.geo.json b/common/src/main/resources/assets/azurelib/geo/item/pistol.geo.json deleted file mode 100644 index dfef26e61..000000000 --- a/common/src/main/resources/assets/azurelib/geo/item/pistol.geo.json +++ /dev/null @@ -1,3690 +0,0 @@ -{ - "format_version": "1.12.0", - "minecraft:geometry": [ - { - "description": { - "identifier": "geometry.unknown", - "texture_width": 128, - "texture_height": 64, - "visible_bounds_width": 3, - "visible_bounds_height": 2.5, - "visible_bounds_offset": [ - 0, - 0.75, - 0 - ] - }, - "bones": [ - { - "name": "group", - "pivot": [ - -0.49601, - 4.03502, - -0.09665 - ], - "cubes": [ - { - "origin": [ - -2, - -0.25, - -9.25 - ], - "size": [ - 3, - 4, - 7.75 - ], - "uv": { - "north": { - "uv": [ - 30, - 18 - ], - "uv_size": [ - 3, - 4 - ] - }, - "east": { - "uv": [ - 14, - 12 - ], - "uv_size": [ - 8, - 4 - ] - }, - "south": { - "uv": [ - 25, - 30 - ], - "uv_size": [ - 3, - 4 - ] - }, - "west": { - "uv": [ - 14, - 16 - ], - "uv_size": [ - 8, - 4 - ] - }, - "up": { - "uv": [ - 5, - 19 - ], - "uv_size": [ - 3, - 8 - ] - }, - "down": { - "uv": [ - 19, - 28 - ], - "uv_size": [ - 3, - -8 - ] - } - } - }, - { - "origin": [ - -1.375, - 1.25, - -11.75 - ], - "size": [ - 1.75, - 2.5, - 4.25 - ], - "uv": { - "north": { - "uv": [ - 37, - 6 - ], - "uv_size": [ - 2, - 3 - ] - }, - "east": { - "uv": [ - 30, - 26 - ], - "uv_size": [ - 4, - 3 - ] - }, - "south": { - "uv": [ - 16, - 37 - ], - "uv_size": [ - 2, - 3 - ] - }, - "west": { - "uv": [ - 28, - 30 - ], - "uv_size": [ - 4, - 3 - ] - }, - "up": { - "uv": [ - 17, - 29 - ], - "uv_size": [ - 2, - 4 - ] - }, - "down": { - "uv": [ - 3, - 40 - ], - "uv_size": [ - 2, - -4 - ] - } - } - }, - { - "origin": [ - -2.125, - 2, - -2.875 - ], - "size": [ - 3.25, - 1.5, - 2.75 - ], - "pivot": [ - -0.5, - 2.25, - -2.5 - ], - "rotation": [ - 45, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 27, - 4 - ], - "uv_size": [ - 3, - 2 - ] - }, - "east": { - "uv": [ - 37, - 18 - ], - "uv_size": [ - 3, - 2 - ] - }, - "south": { - "uv": [ - 37, - 20 - ], - "uv_size": [ - 3, - 2 - ] - }, - "west": { - "uv": [ - 37, - 22 - ], - "uv_size": [ - 3, - 2 - ] - }, - "up": { - "uv": [ - 32, - 33 - ], - "uv_size": [ - 3, - 3 - ] - }, - "down": { - "uv": [ - 34, - 9 - ], - "uv_size": [ - 3, - -3 - ] - } - } - }, - { - "origin": [ - -2.25, - 3.75, - -12 - ], - "size": [ - 3.5, - 3, - 18.5 - ], - "uv": { - "north": { - "uv": [ - 31, - 0 - ], - "uv_size": [ - 4, - 3 - ] - }, - "east": { - "uv": [ - 8, - 0 - ], - "uv_size": [ - 19, - 3 - ] - }, - "south": { - "uv": [ - 31, - 3 - ], - "uv_size": [ - 4, - 3 - ] - }, - "west": { - "uv": [ - 8, - 3 - ], - "uv_size": [ - 19, - 3 - ] - }, - "up": { - "uv": [ - 0, - 0 - ], - "uv_size": [ - 4, - 19 - ] - }, - "down": { - "uv": [ - 4, - 19 - ], - "uv_size": [ - 4, - -19 - ] - } - } - }, - { - "origin": [ - -1.875, - 4.75, - -11.875 - ], - "size": [ - 2.75, - 3, - 18.25 - ], - "uv": { - "north": { - "uv": [ - 14, - 34 - ], - "uv_size": [ - 3, - 3 - ] - }, - "east": { - "uv": [ - 8, - 6 - ], - "uv_size": [ - 18, - 3 - ] - }, - "south": { - "uv": [ - 34, - 21 - ], - "uv_size": [ - 3, - 3 - ] - }, - "west": { - "uv": [ - 8, - 9 - ], - "uv_size": [ - 18, - 3 - ] - }, - "up": { - "uv": [ - 8, - 12 - ], - "uv_size": [ - 3, - 18 - ] - }, - "down": { - "uv": [ - 11, - 30 - ], - "uv_size": [ - 3, - -18 - ] - } - } - }, - { - "origin": [ - -2.125, - -2.89866, - 3.40497 - ], - "size": [ - 3.25, - 4.5, - 3.5 - ], - "pivot": [ - -0.5, - 0.57009, - 6.21747 - ], - "rotation": [ - 22.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 5, - 27 - ], - "uv_size": [ - 3, - 5 - ] - }, - "east": { - "uv": [ - 22, - 12 - ], - "uv_size": [ - 4, - 5 - ] - }, - "south": { - "uv": [ - 19, - 28 - ], - "uv_size": [ - 3, - 5 - ] - }, - "west": { - "uv": [ - 22, - 17 - ], - "uv_size": [ - 4, - 5 - ] - }, - "up": { - "uv": [ - 31, - 6 - ], - "uv_size": [ - 3, - 4 - ] - }, - "down": { - "uv": [ - 31, - 26 - ], - "uv_size": [ - 3, - -4 - ] - } - } - }, - { - "origin": [ - -2, - -5.46979, - 4.98488 - ], - "size": [ - 3, - 3.125, - 3.125 - ], - "uv": { - "north": { - "uv": [ - 34, - 24 - ], - "uv_size": [ - 3, - 3 - ] - }, - "east": { - "uv": [ - 25, - 34 - ], - "uv_size": [ - 3, - 3 - ] - }, - "south": { - "uv": [ - 35, - 0 - ], - "uv_size": [ - 3, - 3 - ] - }, - "west": { - "uv": [ - 35, - 3 - ], - "uv_size": [ - 3, - 3 - ] - }, - "up": { - "uv": [ - 8, - 35 - ], - "uv_size": [ - 3, - 3 - ] - }, - "down": { - "uv": [ - 35, - 12 - ], - "uv_size": [ - 3, - -3 - ] - } - } - }, - { - "origin": [ - -2, - 0.47634, - 3.27997 - ], - "size": [ - 3, - 4.125, - 3.125 - ], - "uv": { - "north": { - "uv": [ - 0, - 32 - ], - "uv_size": [ - 3, - 4 - ] - }, - "east": { - "uv": [ - 3, - 32 - ], - "uv_size": [ - 3, - 4 - ] - }, - "south": { - "uv": [ - 32, - 29 - ], - "uv_size": [ - 3, - 4 - ] - }, - "west": { - "uv": [ - 17, - 33 - ], - "uv_size": [ - 3, - 4 - ] - }, - "up": { - "uv": [ - 11, - 35 - ], - "uv_size": [ - 3, - 3 - ] - }, - "down": { - "uv": [ - 35, - 15 - ], - "uv_size": [ - 3, - -3 - ] - } - } - }, - { - "origin": [ - -1.75, - -2.64866, - 2.65497 - ], - "size": [ - 2.5, - 4.75, - 4.75 - ], - "pivot": [ - -0.5, - 0.57009, - 6.21747 - ], - "rotation": [ - 22.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 14, - 29 - ], - "uv_size": [ - 3, - 5 - ] - }, - "east": { - "uv": [ - 0, - 19 - ], - "uv_size": [ - 5, - 5 - ] - }, - "south": { - "uv": [ - 8, - 30 - ], - "uv_size": [ - 3, - 5 - ] - }, - "west": { - "uv": [ - 14, - 20 - ], - "uv_size": [ - 5, - 5 - ] - }, - "up": { - "uv": [ - 11, - 30 - ], - "uv_size": [ - 3, - 5 - ] - }, - "down": { - "uv": [ - 22, - 35 - ], - "uv_size": [ - 3, - -5 - ] - } - } - }, - { - "origin": [ - -1.625, - -4.96979, - 4.23488 - ], - "size": [ - 2.25, - 3.125, - 4.25 - ], - "uv": { - "north": { - "uv": [ - 18, - 37 - ], - "uv_size": [ - 2, - 3 - ] - }, - "east": { - "uv": [ - 33, - 18 - ], - "uv_size": [ - 4, - 3 - ] - }, - "south": { - "uv": [ - 37, - 24 - ], - "uv_size": [ - 2, - 3 - ] - }, - "west": { - "uv": [ - 28, - 33 - ], - "uv_size": [ - 4, - 3 - ] - }, - "up": { - "uv": [ - 36, - 27 - ], - "uv_size": [ - 2, - 4 - ] - }, - "down": { - "uv": [ - 28, - 40 - ], - "uv_size": [ - 2, - -4 - ] - } - } - }, - { - "origin": [ - -1.625, - 0.60134, - 2.40497 - ], - "size": [ - 2.25, - 4, - 4.375 - ], - "uv": { - "north": { - "uv": [ - 30, - 36 - ], - "uv_size": [ - 2, - 4 - ] - }, - "east": { - "uv": [ - 26, - 18 - ], - "uv_size": [ - 4, - 4 - ] - }, - "south": { - "uv": [ - 36, - 31 - ], - "uv_size": [ - 2, - 4 - ] - }, - "west": { - "uv": [ - 22, - 26 - ], - "uv_size": [ - 4, - 4 - ] - }, - "up": { - "uv": [ - 32, - 36 - ], - "uv_size": [ - 2, - 4 - ] - }, - "down": { - "uv": [ - 34, - 40 - ], - "uv_size": [ - 2, - -4 - ] - } - } - }, - { - "origin": [ - -1.5, - 0.875, - -1.5 - ], - "size": [ - 2, - 0.5, - 6 - ], - "uv": { - "north": { - "uv": [ - 30, - 29 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 38, - 0 - ], - "uv_size": [ - 6, - 1 - ] - }, - "south": { - "uv": [ - 40, - 21 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 38, - 1 - ], - "uv_size": [ - 6, - 1 - ] - }, - "up": { - "uv": [ - 6, - 32 - ], - "uv_size": [ - 2, - 6 - ] - }, - "down": { - "uv": [ - 20, - 39 - ], - "uv_size": [ - 2, - -6 - ] - } - } - }, - { - "origin": [ - -2, - 3.875, - 4.5 - ], - "size": [ - 3, - 1, - 3 - ], - "uv": { - "north": { - "uv": [ - 39, - 38 - ], - "uv_size": [ - 3, - 1 - ] - }, - "east": { - "uv": [ - 40, - 4 - ], - "uv_size": [ - 3, - 1 - ] - }, - "south": { - "uv": [ - 40, - 10 - ], - "uv_size": [ - 3, - 1 - ] - }, - "west": { - "uv": [ - 40, - 11 - ], - "uv_size": [ - 3, - 1 - ] - }, - "up": { - "uv": [ - 35, - 15 - ], - "uv_size": [ - 3, - 3 - ] - }, - "down": { - "uv": [ - 22, - 38 - ], - "uv_size": [ - 3, - -3 - ] - } - } - }, - { - "origin": [ - -1.625, - 4, - 7.5 - ], - "size": [ - 2.25, - 0.75, - 2 - ], - "uv": { - "north": { - "uv": [ - 40, - 22 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 40, - 23 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 25, - 40 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 28, - 40 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 8, - 38 - ], - "uv_size": [ - 2, - 2 - ] - }, - "down": { - "uv": [ - 38, - 11 - ], - "uv_size": [ - 2, - -2 - ] - } - } - }, - { - "origin": [ - 1, - 0.625, - -7.5 - ], - "size": [ - 0.5, - 4.125, - 5 - ], - "uv": { - "north": { - "uv": [ - 4, - 28 - ], - "uv_size": [ - 1, - 4 - ] - }, - "east": { - "uv": [ - 22, - 22 - ], - "uv_size": [ - 5, - 4 - ] - }, - "south": { - "uv": [ - 10, - 38 - ], - "uv_size": [ - 1, - 4 - ] - }, - "west": { - "uv": [ - 0, - 24 - ], - "uv_size": [ - 5, - 4 - ] - }, - "up": { - "uv": [ - 5, - 36 - ], - "uv_size": [ - 1, - 5 - ] - }, - "down": { - "uv": [ - 27, - 42 - ], - "uv_size": [ - 1, - -5 - ] - } - } - }, - { - "origin": [ - -2.5, - 0.625, - -7.5 - ], - "size": [ - 0.5, - 4.125, - 5 - ], - "uv": { - "north": { - "uv": [ - 11, - 38 - ], - "uv_size": [ - 1, - 4 - ] - }, - "east": { - "uv": [ - 14, - 25 - ], - "uv_size": [ - 5, - 4 - ] - }, - "south": { - "uv": [ - 38, - 11 - ], - "uv_size": [ - 1, - 4 - ] - }, - "west": { - "uv": [ - 26, - 6 - ], - "uv_size": [ - 5, - 4 - ] - }, - "up": { - "uv": [ - 6, - 38 - ], - "uv_size": [ - 1, - 5 - ] - }, - "down": { - "uv": [ - 7, - 43 - ], - "uv_size": [ - 1, - -5 - ] - } - } - }, - { - "origin": [ - 1.25, - 4.625, - -10 - ], - "size": [ - 0.75, - 1.75, - 9 - ], - "uv": { - "north": { - "uv": [ - 30, - 4 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 26, - 10 - ], - "uv_size": [ - 9, - 2 - ] - }, - "south": { - "uv": [ - 34, - 27 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 26, - 12 - ], - "uv_size": [ - 9, - 2 - ] - }, - "up": { - "uv": [ - 35, - 27 - ], - "uv_size": [ - 1, - 9 - ] - }, - "down": { - "uv": [ - 0, - 45 - ], - "uv_size": [ - 1, - -9 - ] - } - } - }, - { - "origin": [ - -2.875, - 4.625, - -10 - ], - "size": [ - 0.625, - 1.75, - 9 - ], - "uv": { - "north": { - "uv": [ - 19, - 40 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 26, - 14 - ], - "uv_size": [ - 9, - 2 - ] - }, - "south": { - "uv": [ - 30, - 40 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 26, - 16 - ], - "uv_size": [ - 9, - 2 - ] - }, - "up": { - "uv": [ - 1, - 36 - ], - "uv_size": [ - 1, - 9 - ] - }, - "down": { - "uv": [ - 2, - 45 - ], - "uv_size": [ - 1, - -9 - ] - } - } - }, - { - "origin": [ - 0.375, - 2.625, - -11.5 - ], - "size": [ - 1.375, - 3.625, - 4 - ], - "uv": { - "north": { - "uv": [ - 12, - 38 - ], - "uv_size": [ - 1, - 4 - ] - }, - "east": { - "uv": [ - 26, - 26 - ], - "uv_size": [ - 4, - 4 - ] - }, - "south": { - "uv": [ - 13, - 38 - ], - "uv_size": [ - 1, - 4 - ] - }, - "west": { - "uv": [ - 27, - 0 - ], - "uv_size": [ - 4, - 4 - ] - }, - "up": { - "uv": [ - 22, - 38 - ], - "uv_size": [ - 1, - 4 - ] - }, - "down": { - "uv": [ - 23, - 42 - ], - "uv_size": [ - 1, - -4 - ] - } - } - }, - { - "origin": [ - -2.75, - 2.625, - -11.5 - ], - "size": [ - 1.375, - 3.625, - 4 - ], - "uv": { - "north": { - "uv": [ - 24, - 38 - ], - "uv_size": [ - 1, - 4 - ] - }, - "east": { - "uv": [ - 27, - 22 - ], - "uv_size": [ - 4, - 4 - ] - }, - "south": { - "uv": [ - 38, - 27 - ], - "uv_size": [ - 1, - 4 - ] - }, - "west": { - "uv": [ - 0, - 28 - ], - "uv_size": [ - 4, - 4 - ] - }, - "up": { - "uv": [ - 38, - 31 - ], - "uv_size": [ - 1, - 4 - ] - }, - "down": { - "uv": [ - 38, - 39 - ], - "uv_size": [ - 1, - -4 - ] - } - } - }, - { - "origin": [ - -3.5, - -0.125, - -5.875 - ], - "size": [ - 1.5, - 0.75, - 4 - ], - "uv": { - "north": { - "uv": [ - 31, - 40 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 38, - 5 - ], - "uv_size": [ - 4, - 1 - ] - }, - "south": { - "uv": [ - 33, - 40 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 38, - 15 - ], - "uv_size": [ - 4, - 1 - ] - }, - "up": { - "uv": [ - 36, - 35 - ], - "uv_size": [ - 2, - 4 - ] - }, - "down": { - "uv": [ - 14, - 41 - ], - "uv_size": [ - 2, - -4 - ] - } - } - }, - { - "origin": [ - -0.75, - -0.875, - -7.5 - ], - "size": [ - 0.5, - 0.625, - 4 - ], - "uv": { - "north": { - "uv": [ - 33, - 21 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 38, - 16 - ], - "uv_size": [ - 4, - 1 - ] - }, - "south": { - "uv": [ - 34, - 9 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 38, - 17 - ], - "uv_size": [ - 4, - 1 - ] - }, - "up": { - "uv": [ - 39, - 11 - ], - "uv_size": [ - 1, - 4 - ] - }, - "down": { - "uv": [ - 20, - 43 - ], - "uv_size": [ - 1, - -4 - ] - } - } - }, - { - "origin": [ - -1.25, - 1.125, - 6.5 - ], - "size": [ - 1.5, - 2.875, - 0.75 - ], - "pivot": [ - -0.5, - 2.5625, - 6.875 - ], - "rotation": [ - 22.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 25, - 37 - ], - "uv_size": [ - 2, - 3 - ] - }, - "east": { - "uv": [ - 21, - 39 - ], - "uv_size": [ - 1, - 3 - ] - }, - "south": { - "uv": [ - 38, - 2 - ], - "uv_size": [ - 2, - 3 - ] - }, - "west": { - "uv": [ - 40, - 12 - ], - "uv_size": [ - 1, - 3 - ] - }, - "up": { - "uv": [ - 40, - 39 - ], - "uv_size": [ - 2, - 1 - ] - }, - "down": { - "uv": [ - 40, - 41 - ], - "uv_size": [ - 2, - -1 - ] - } - } - }, - { - "origin": [ - -0.625, - 1.40248, - 1.84024 - ], - "size": [ - 0.25, - 2.875, - 0.625 - ], - "pivot": [ - -0.5, - 3.08998, - 2.02774 - ], - "rotation": [ - -45, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 16, - 40 - ], - "uv_size": [ - 1, - 3 - ] - }, - "east": { - "uv": [ - 17, - 40 - ], - "uv_size": [ - 1, - 3 - ] - }, - "south": { - "uv": [ - 18, - 40 - ], - "uv_size": [ - 1, - 3 - ] - }, - "west": { - "uv": [ - 40, - 18 - ], - "uv_size": [ - 1, - 3 - ] - }, - "up": { - "uv": [ - 39, - 8 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 42, - 24 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -0.75, - 2.52748, - 1.46524 - ], - "size": [ - 0.5, - 1.75, - 1 - ], - "pivot": [ - -0.5, - 3.08998, - 2.02774 - ], - "rotation": [ - -22.5, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 35, - 40 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 5, - 41 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 41, - 6 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 41, - 12 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 24, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 42, - 26 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -1.375, - 7.75, - 4.5 - ], - "size": [ - 1.75, - 0.375, - 1.875 - ], - "uv": { - "north": { - "uv": [ - 14, - 41 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 41, - 14 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 41, - 18 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 41, - 19 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 39, - 6 - ], - "uv_size": [ - 2, - 2 - ] - }, - "down": { - "uv": [ - 39, - 26 - ], - "uv_size": [ - 2, - -2 - ] - } - } - }, - { - "origin": [ - 0.125, - 8.125, - 4.5 - ], - "size": [ - 0.25, - 0.375, - 1.875 - ], - "uv": { - "north": { - "uv": [ - 42, - 26 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 41, - 20 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 27, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 41, - 24 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 25, - 41 - ], - "uv_size": [ - 1, - 2 - ] - }, - "down": { - "uv": [ - 41, - 27 - ], - "uv_size": [ - 1, - -2 - ] - } - } - }, - { - "origin": [ - 1.25, - 4.825, - 4 - ], - "size": [ - 0.25, - 0.875, - 1.5 - ], - "uv": { - "north": { - "uv": [ - 28, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 41, - 27 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 42, - 28 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 28, - 41 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 26, - 41 - ], - "uv_size": [ - 1, - 2 - ] - }, - "down": { - "uv": [ - 41, - 30 - ], - "uv_size": [ - 1, - -2 - ] - } - } - }, - { - "origin": [ - -2.5, - 4.825, - 4 - ], - "size": [ - 0.25, - 0.875, - 1.5 - ], - "uv": { - "north": { - "uv": [ - 29, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 41, - 30 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 42, - 29 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 31, - 41 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 41, - 31 - ], - "uv_size": [ - 1, - 2 - ] - }, - "down": { - "uv": [ - 33, - 43 - ], - "uv_size": [ - 1, - -2 - ] - } - } - }, - { - "origin": [ - 1.25, - 4.825, - 5.5 - ], - "size": [ - 0.625, - 0.875, - 0.875 - ], - "uv": { - "north": { - "uv": [ - 30, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 31, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 42, - 31 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 32, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 42, - 32 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 35, - 43 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -2.875, - 4.825, - 5.5 - ], - "size": [ - 0.625, - 0.875, - 0.875 - ], - "uv": { - "north": { - "uv": [ - 42, - 35 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 42, - 36 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 42, - 38 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 42, - 39 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 42, - 40 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 42, - 42 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - 0.125, - 8.5, - 5 - ], - "size": [ - 0.25, - 0.375, - 1.125 - ], - "uv": { - "north": { - "uv": [ - 42, - 42 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 43, - 2 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 43, - 3 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 43, - 4 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 5, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 43, - 6 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -1.375, - 8.125, - 4.5 - ], - "size": [ - 0.25, - 0.375, - 1.875 - ], - "uv": { - "north": { - "uv": [ - 6, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 41, - 33 - ], - "uv_size": [ - 2, - 1 - ] - }, - "south": { - "uv": [ - 43, - 6 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 41, - 34 - ], - "uv_size": [ - 2, - 1 - ] - }, - "up": { - "uv": [ - 34, - 41 - ], - "uv_size": [ - 1, - 2 - ] - }, - "down": { - "uv": [ - 41, - 37 - ], - "uv_size": [ - 1, - -2 - ] - } - } - }, - { - "origin": [ - -1.375, - 8.5, - 5 - ], - "size": [ - 0.25, - 0.375, - 1.125 - ], - "uv": { - "north": { - "uv": [ - 7, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 43, - 8 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 43, - 9 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 43, - 10 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 11, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 43, - 12 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -0.75, - 7.75, - -11 - ], - "size": [ - 0.5, - 0.375, - 1.375 - ], - "uv": { - "north": { - "uv": [ - 12, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 13, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 14, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 43, - 14 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 16, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 17, - 44 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - 1, - 3.375, - -14.625 - ], - "size": [ - 0.75, - 2, - 2 - ], - "pivot": [ - 13.5, - -5.25, - -14.5 - ], - "rotation": [ - -45, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 36, - 41 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 39, - 26 - ], - "uv_size": [ - 2, - 2 - ] - }, - "south": { - "uv": [ - 37, - 41 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 39, - 28 - ], - "uv_size": [ - 2, - 2 - ] - }, - "up": { - "uv": [ - 38, - 41 - ], - "uv_size": [ - 1, - 2 - ] - }, - "down": { - "uv": [ - 39, - 43 - ], - "uv_size": [ - 1, - -2 - ] - } - } - }, - { - "origin": [ - -2.75, - 3.375, - -14.625 - ], - "size": [ - 0.75, - 2, - 2 - ], - "pivot": [ - -13.5, - -5.25, - -14.5 - ], - "rotation": [ - -45, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 40, - 41 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 39, - 30 - ], - "uv_size": [ - 2, - 2 - ] - }, - "south": { - "uv": [ - 41, - 41 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 39, - 32 - ], - "uv_size": [ - 2, - 2 - ] - }, - "up": { - "uv": [ - 42, - 2 - ], - "uv_size": [ - 1, - 2 - ] - }, - "down": { - "uv": [ - 3, - 44 - ], - "uv_size": [ - 1, - -2 - ] - } - } - }, - { - "origin": [ - 1.75, - 3.875, - -14.125 - ], - "size": [ - 0.25, - 1, - 1 - ], - "pivot": [ - 13.5, - -5.25, - -14.5 - ], - "rotation": [ - -45, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 18, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 43, - 18 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 43, - 19 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 20, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 43, - 20 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 21, - 44 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -3, - 3.875, - -14.125 - ], - "size": [ - 0.25, - 1, - 1 - ], - "pivot": [ - -13.5, - -5.25, - -14.5 - ], - "rotation": [ - -45, - 0, - 0 - ], - "uv": { - "north": { - "uv": [ - 43, - 21 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 22, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 43, - 22 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 43, - 23 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 24, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 43, - 25 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -1.5, - 4.25, - -13.5 - ], - "size": [ - 2, - 2, - 1.5 - ], - "pivot": [ - -0.5, - 5.25, - -12.75 - ], - "rotation": [ - 0, - 0, - -45 - ], - "uv": { - "north": { - "uv": [ - 39, - 34 - ], - "uv_size": [ - 2, - 2 - ] - }, - "east": { - "uv": [ - 36, - 39 - ], - "uv_size": [ - 2, - 2 - ] - }, - "south": { - "uv": [ - 39, - 36 - ], - "uv_size": [ - 2, - 2 - ] - }, - "west": { - "uv": [ - 38, - 39 - ], - "uv_size": [ - 2, - 2 - ] - }, - "up": { - "uv": [ - 40, - 2 - ], - "uv_size": [ - 2, - 2 - ] - }, - "down": { - "uv": [ - 3, - 42 - ], - "uv_size": [ - 2, - -2 - ] - } - } - }, - { - "origin": [ - -1.5, - 5.25, - 6.375 - ], - "size": [ - 2, - 2, - 0.25 - ], - "pivot": [ - -0.5, - 6.25, - 7.5 - ], - "rotation": [ - 0, - 0, - 45 - ], - "uv": { - "north": { - "uv": [ - 8, - 40 - ], - "uv_size": [ - 2, - 2 - ] - }, - "east": { - "uv": [ - 4, - 42 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 40, - 8 - ], - "uv_size": [ - 2, - 2 - ] - }, - "west": { - "uv": [ - 42, - 5 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 41, - 37 - ], - "uv_size": [ - 2, - 1 - ] - }, - "down": { - "uv": [ - 42, - 8 - ], - "uv_size": [ - 2, - -1 - ] - } - } - }, - { - "origin": [ - -0.75, - 6, - 6.625 - ], - "size": [ - 0.5, - 0.5, - 0.125 - ], - "pivot": [ - -0.5, - 6.25, - 7.5 - ], - "rotation": [ - 0, - 0, - 45 - ], - "uv": { - "north": { - "uv": [ - 25, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "east": { - "uv": [ - 43, - 25 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 26, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "west": { - "uv": [ - 43, - 26 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 27, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 43, - 28 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - 0.25, - 5.375, - 6.625 - ], - "size": [ - 0.125, - 1.75, - 0.125 - ], - "pivot": [ - -0.5, - 6.25, - 7.5 - ], - "rotation": [ - 0, - 0, - 45 - ], - "uv": { - "north": { - "uv": [ - 8, - 42 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 42, - 8 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 9, - 42 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 10, - 42 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 28, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 43, - 29 - ], - "uv_size": [ - 1, - -1 - ] - } - } - }, - { - "origin": [ - -1.25, - 7, - 6.625 - ], - "size": [ - 1.5, - 0.125, - 0.125 - ], - "pivot": [ - -0.5, - 6.25, - 7.5 - ], - "rotation": [ - 0, - 0, - 45 - ], - "uv": { - "north": { - "uv": [ - 11, - 42 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 29, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 42, - 12 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 43, - 29 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 13, - 42 - ], - "uv_size": [ - 2, - 1 - ] - }, - "down": { - "uv": [ - 42, - 14 - ], - "uv_size": [ - 2, - -1 - ] - } - } - }, - { - "origin": [ - -1.25, - 5.375, - 6.625 - ], - "size": [ - 1.5, - 0.125, - 0.125 - ], - "pivot": [ - -0.5, - 6.25, - 7.5 - ], - "rotation": [ - 0, - 0, - 45 - ], - "uv": { - "north": { - "uv": [ - 42, - 15 - ], - "uv_size": [ - 2, - 1 - ] - }, - "east": { - "uv": [ - 30, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "south": { - "uv": [ - 42, - 16 - ], - "uv_size": [ - 2, - 1 - ] - }, - "west": { - "uv": [ - 43, - 30 - ], - "uv_size": [ - 1, - 1 - ] - }, - "up": { - "uv": [ - 42, - 17 - ], - "uv_size": [ - 2, - 1 - ] - }, - "down": { - "uv": [ - 21, - 43 - ], - "uv_size": [ - 2, - -1 - ] - } - } - }, - { - "origin": [ - -1.375, - 5.375, - 6.625 - ], - "size": [ - 0.125, - 1.75, - 0.125 - ], - "pivot": [ - -0.5, - 6.25, - 7.5 - ], - "rotation": [ - 0, - 0, - 45 - ], - "uv": { - "north": { - "uv": [ - 15, - 42 - ], - "uv_size": [ - 1, - 2 - ] - }, - "east": { - "uv": [ - 19, - 42 - ], - "uv_size": [ - 1, - 2 - ] - }, - "south": { - "uv": [ - 42, - 21 - ], - "uv_size": [ - 1, - 2 - ] - }, - "west": { - "uv": [ - 23, - 42 - ], - "uv_size": [ - 1, - 2 - ] - }, - "up": { - "uv": [ - 31, - 43 - ], - "uv_size": [ - 1, - 1 - ] - }, - "down": { - "uv": [ - 43, - 32 - ], - "uv_size": [ - 1, - -1 - ] - } - } - } - ] - }, - { - "name": "bone", - "parent": "group", - "pivot": [ - -0.49601, - 5.23502, - -13.29665 - ], - "cubes": [ - { - "origin": [ - -0.99601, - 4.73502, - -13.29665 - ], - "size": [ - 1, - 1, - 0 - ], - "uv": { - "north": { - "uv": [ - 128, - 0 - ], - "uv_size": [ - -64, - 64 - ] - }, - "east": { - "uv": [ - 128, - 0 - ], - "uv_size": [ - 0, - 1 - ] - }, - "south": { - "uv": [ - 64, - 0 - ], - "uv_size": [ - 64, - 64 - ] - }, - "west": { - "uv": [ - 128, - 0 - ], - "uv_size": [ - 0, - 1 - ] - }, - "up": { - "uv": [ - 128, - 1 - ], - "uv_size": [ - 0, - -1 - ] - }, - "down": { - "uv": [ - 128, - 1 - ], - "uv_size": [ - 0, - -1 - ] - } - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/models/block/stargate.json b/common/src/main/resources/assets/azurelib/models/block/stargate.json deleted file mode 100644 index 8997086ce..000000000 --- a/common/src/main/resources/assets/azurelib/models/block/stargate.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "credit": "Made with Blockbench", - "parent": "builtin/entity", - "textures": { - "particle": "azurelib:block/stargate" - }, - "texture_size": [ - 256, - 256 - ], - "display": { - "thirdperson_righthand": { - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "thirdperson_lefthand": { - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "firstperson_righthand": { - "rotation": [ - 0, - -64, - 0 - ], - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "firstperson_lefthand": { - "rotation": [ - 0, - -64, - 0 - ], - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "ground": { - "translation": [ - 0, - 1.75, - 0 - ], - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "gui": { - "rotation": [ - 0, - -36, - 0 - ], - "translation": [ - 0, - -6, - 0 - ], - "scale": [ - 0.18, - 0.18, - 0.18 - ] - }, - "head": { - "scale": [ - 0, - 0, - 0 - ] - }, - "fixed": { - "translation": [ - 0, - -6, - 0 - ], - "scale": [ - 0.2, - 0.2, - 0.2 - ] - } - } -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/models/item/doomicorn_boots.json b/common/src/main/resources/assets/azurelib/models/item/doomicorn_boots.json deleted file mode 100644 index befab1952..000000000 --- a/common/src/main/resources/assets/azurelib/models/item/doomicorn_boots.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "azurelib:item/doomicorn_boots" - } -} diff --git a/common/src/main/resources/assets/azurelib/models/item/doomicorn_chestplate.json b/common/src/main/resources/assets/azurelib/models/item/doomicorn_chestplate.json deleted file mode 100644 index 96425b55a..000000000 --- a/common/src/main/resources/assets/azurelib/models/item/doomicorn_chestplate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "azurelib:item/doomicorn_chestplate" - } -} diff --git a/common/src/main/resources/assets/azurelib/models/item/doomicorn_helmet.json b/common/src/main/resources/assets/azurelib/models/item/doomicorn_helmet.json deleted file mode 100644 index 0229c662d..000000000 --- a/common/src/main/resources/assets/azurelib/models/item/doomicorn_helmet.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "azurelib:item/doomicorn_helmet" - } -} diff --git a/common/src/main/resources/assets/azurelib/models/item/doomicorn_leggings.json b/common/src/main/resources/assets/azurelib/models/item/doomicorn_leggings.json deleted file mode 100644 index 1afbd089c..000000000 --- a/common/src/main/resources/assets/azurelib/models/item/doomicorn_leggings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "azurelib:item/doomicorn_leggings" - } -} diff --git a/common/src/main/resources/assets/azurelib/models/item/pistol.json b/common/src/main/resources/assets/azurelib/models/item/pistol.json deleted file mode 100644 index dd331f022..000000000 --- a/common/src/main/resources/assets/azurelib/models/item/pistol.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "credit": "Made with Blockbench", - "parent": "builtin/entity", - "texture_size": [ - 128, - 64 - ], - "display": { - "thirdperson_righthand": { - "translation": [ - 0, - -1.25, - -0.25 - ], - "scale": [ - 0.5, - 0.5, - 0.5 - ] - }, - "thirdperson_lefthand": { - "translation": [ - 0, - -1.25, - -0.25 - ], - "scale": [ - 0.5, - 0.5, - 0.5 - ] - }, - "firstperson_righthand": { - "translation": [ - 3, - -5.5, - -5.75 - ] - }, - "firstperson_lefthand": { - "translation": [ - 3, - -5.5, - -5.75 - ] - }, - "ground": { - "scale": [ - 0.5, - 0.5, - 0.5 - ] - }, - "gui": { - "rotation": [ - 40, - -38, - 0 - ], - "translation": [ - -1, - -1.25, - 0 - ], - "scale": [ - 0.7, - 0.7, - 0.7 - ] - }, - "head": { - "scale": [ - 0, - 0, - 0 - ] - }, - "fixed": { - "rotation": [ - 0, - -90, - 0 - ], - "translation": [ - -1.75, - -2.5, - -0.25 - ], - "scale": [ - 0.8, - 0.8, - 0.8 - ] - } - } -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/models/item/stargate.json b/common/src/main/resources/assets/azurelib/models/item/stargate.json deleted file mode 100644 index bc6977543..000000000 --- a/common/src/main/resources/assets/azurelib/models/item/stargate.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "credit": "Made with Blockbench", - "parent": "builtin/entity", - "texture_size": [ - 256, - 256 - ], - "display": { - "thirdperson_righthand": { - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "thirdperson_lefthand": { - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "firstperson_righthand": { - "rotation": [ - 0, - -64, - 0 - ], - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "firstperson_lefthand": { - "rotation": [ - 0, - -64, - 0 - ], - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "ground": { - "translation": [ - 0, - 1.75, - 0 - ], - "scale": [ - 0.1, - 0.1, - 0.1 - ] - }, - "gui": { - "rotation": [ - 0, - -36, - 0 - ], - "translation": [ - 0, - -6, - 0 - ], - "scale": [ - 0.18, - 0.18, - 0.18 - ] - }, - "head": { - "scale": [ - 0, - 0, - 0 - ] - }, - "fixed": { - "translation": [ - 0, - -6, - 0 - ], - "scale": [ - 0.2, - 0.2, - 0.2 - ] - } - } -} \ No newline at end of file diff --git a/common/src/main/resources/assets/azurelib/textures/block/stargate.png b/common/src/main/resources/assets/azurelib/textures/block/stargate.png deleted file mode 100644 index 7ef23d6292b596c40d528236854ba1b20c63a3da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8667 zcmdsdc{tQ>`}SuT`<^MXCm{(TCQFP+ickoVo$O2Yb&TwxkZjqKLiX&kZ)F*UhVVrb zqb4&XTVr`|-|z2v-sgG$dH;L=cpZ+z%pCXUcHh^1Ue|S==RNTTH?>(9c^DxGV$r## zc?*JI;3W*Arv-m3d|o>qy?by=TLY>b<@*jnLXeIo$|x{*^||XKUfff{;pTE(%X<$) zpBvdvWm(L8YzMjTcCntAVtY>y%T1z-V-Fed{;Be~v(uKATZo(aSf}9cQ-c1SfsuVu zRDrUbp7u_w>M#Xm<*7~>ZI_UNqdRsg4Vh8L_-4xIoJ&BR&WZQIcUEJU&!@T7CfFNq z7!&BYO}3jQPn4S-Ds9g`7+q=_lHL`HW7vHWv6HeECYYs5TS@Hpvkc6mL$M3dQ@PDx z$ab$S5q)`hoz#hqI`^416}GZ6jnv{!QjmZOmJa&>8t5YPrQwk)F)*C>LeM=gzWMF- zsFcu4>sYdy-;*1A1-CW%Y?x8ZXL3fUAcR(=c!G=`=c+$%vjyw4+X+3rC_!7CynFHX zm>++tNSDkbb@?x=3X($_g$8IskO6`w#-`JoPdwohhY&B+xv_W3D!%VwB_juumZ8v) zEYvB(?8O%wdYLVcQ0Q!4tjajQy@+H5Gr&Q2`eD3{?Vu&~#YE(=Ij6>JnCXy@{XFCB_Xy|;TiOgw?g5_f9I$LmFNB@>E$A!KpO zFqzyU<V0ccO{(6red$LCbdZp4H7m` zkE;yCI~&}u)iUG!P7TS*W--g_^?dlih)m`UOxwZ-3D}#(;E|Dv>LPSOkfx-6KtNOk zZw$v?PV@55_*wTVlXAOvvBVZHKDuTLsYp4|mQ!r#OjnD6NRfnvAOVcJ6|VT`le7E8 zgMBZRl!nSH#>P#RT&_|FSyq=hT>>lvUayWbw;n+vUy2H-J*eiMHY1`J^ljaek%} z84>rB-w+TJf(hAjG@d93`R>Io8H--p9w?>ZK0_9Ae+;qL7$D9Bw(!Kj@(53`1Tcxb z-j%C{3(-@j@4%Y&>0UdS4La6!>CIX)b^36ao`-H|mQA`)rsJL?Y=&wz=7~Qh%T@J> zv4Y`KPG^U3RaOInvwJrzn!I2$Gc$urK21JLA1u`7v?Jl@IIUT8^y{2YwQtnzP4fmlj(ov)F+p-xV8^TezjG)<;!F66$w!(Rd`n*w0#b;fb~Y!}8B| z@|AJ#^C2g#`h$DSFIya!o3P$I~`3uhrB>k$;lD4rj^5 z1{l7guK-9v{xnC6Pvy>UsJxvyZ&YqdND^YGX{oY5AKa71Q|&j|K@Y4t<{^g z(W)i#F7B@$YO&ANwYA6SM*)!TVfp&(0-V;CD2Y4DdJl`l?Ow@Y&x!nlr9x*O=AKRx zNebjq=={K6e+gLNz^S5bxMi5B>&;nKCa6p^j(B@dsqP^hZ5p^mgg>Od0Be|? z66Aa=!oW9RgpO;DPfCUsHQoNz3X5k@l*)Cf@D3|GdfY59hJke7Ec-Ns+B|} z_L)We)GXkJYR2qkPZm7rX(fe!_lmfOPJIs#g3XoF*Gj&ab}RA;=EV7L3F#ag%^0e7 z=S{|U`dOx5KNG4=-M$fV`L(9x({3W|2?l`a!uT|{sSU(W#D2M;apj53Rp;w1KMk0Vpcn>| z*CUp)S@)r8C9fE!U!&Lv(^G-ePv{f=D}<~%+ZH6ZW?EFn%|-JIiZd0dkw$}h9HvG; zHQm6HjJLkT+Ub2|RmYdnqjBc7kwoC`(zESnFl=75`%ITpkvW8+Kjm>=z0^oaW&rD0 zS9k$(en z23Kb}z4*9N<0N}>!sTN6kb``VUSJ-VGA(*MEuYfgPaD2Zw8Gyw!+E&(*UTC$v>3MM zcRF@tWQ5HHmM)yvL}?s6E@JVQS*1C23=%%!*|G0m&1g_z5+&YI;>-?*g@jC6A4g9U zH-oXHPFgLu2DQMW%C-w6McPj@)cj3V-0o z6|zthUVLnVDs&Sd>;qjrr1s9)CM-;~@Er2)(Zju~^aw>-_Hu3OnmDV~%$@#bmZ^3o zRO;Gp8ifTFm>27uZN#L`ZqsW?6UR$!VC?tR9GYn`lz6(9{{zaSL+|Ib8GSgfm#)V6 zfm+D6%LHcX9ubh9PuR6SZk?%GssfeKWHKMcLdO?wOC+rX%eGeX&i*vq(xJfEEcHc+Mdhuef6`Q-0nWK6NxWvxH%OJL=74 z1?g=SwhRZFA#~5rmEm=<5o+s~U6}=f5D_t~a8T{u>V!{`R|o6BG=EJf5=wjjTjUSO z#XQXdl)oXut`GY9n;Mkww^L#)OWj9_;%mvKf+ocXR+uow*uYs(G1E6*ej5G*(>zX* zywM)PeKf|%n5ABOawK+ST{|AuJ8dCS1|COgfOt9HoK->Vc=OSPOj_rfDY0pi=ztl+;~780qBnR*&onRa2%ev z!fL18R@xzF^wF3%t}qan1*DX(75eY!*?z8vyltU?IlnvqIfy&07b@DK>8ycEjpbgU zQXkGW>pS}38np=2s~dlpiT#zJ`UTs6CK*m|3WB`1-Nr)_1Mbc`Ph#j#})UWIahkMm`RUtm85 zVXj>ef#6SUuR-*}ePa@x$G!pr7qU_q8;oh)CoZX}9DaMN@9Jze0SOIKo8G+d5X*i1 z|9A>SMl%64!Z-w2J^R_1Ur*R0 zAl310Aow)q8pk)-f1m%q_n%o>K_$<_veTZTD@u*?TWpa6+*yboRQv|_uI&pG0`-jQ z?7cg>dQk1OB&2SLh%2HWm;dO|_+)75@rdKc3HhB<{urTDCl{&$amLVyi1@!`nAcQN z0P|1A7dZ3CCQwQD9&`xojE~_TJX6yr9>On(-aZa0@u>u1K$azk?$?_Gl94KcLeQ9g z_EJW%zn7~QDj-vU!~pe9jT%jC0o1si%R`J_IH+V9_H82w6a%vi(EAFBN?^5RW%uQI zRJQ<}%`0MjnjbmVF<>`J{IYwwd-cc%i@Rc+%FoP>Delik{1ARNJN;;20&X1;pt#d$ z!`;=fy|slNl|Xg81IX*(-4r%F51}57PQZnvtad-#2_34np7CEmt+XVArYpck-cT~HO8_68 zc8UPt0zsDp^Qe)m?F7J-015nCn+3P&D(u^@zarwQ0J#5~4iD#j(W#Z88gr=^qSsi{ z$`D~t!2~$EcG#3a+IhW*_GfXw+^L(f7#5e$g^~ zXv59kw_g?5Iv3+$CXQfSQ<2WZC?!*C87ei4D?mUq0qvNK2ff}~pBVOynWb@Fs?f`q zy2N>OEkR_cTN4L%7d3iZtiZM0-gn-4zVIbT_ z%$I4-=M_Q~6%{n=qM}r~e6IC8$YiFxy>{}8kN2$|9B8V}X{mT`dpz_xA|N6N#!>52Jq0MQVdRS$letJNY`M-D3Ko%sFqqz13*7oSk=q z;rJ@Ro!1i_7K!;vtV@{*Y4gFRp7epQ$DIHWs zBA4`^pA1u$=fL?{b6+*MAu}wX_}modSG>FLq;*7mfMmrZcYc&g+CPo`he9vDQat~r zs!HtF^m1e=0~CT`z@Xg}m@ILNEu^va6xFRvwQDj6t&_-h3#n~--BKfsdh@`Sqix85d+8q zSwG-yZFZuGKE$FuVvDKMY=I10@F#TKetrmf3GUXWoUB?D>zaV8r;2dLzZL%p8N^# zR35eSZZk1uWeY)UAA{vC{SNZh5`9 zqB#Fn78E;D>k_NXMK*NUVP>LRw&TjDi6+bix6oSyIxc%}tqHo(%Z+i7ze5 zpxm9c^-xN-ewd{H1F4ggPm8I}pA?P%8vY4bEY>b*#zPI5`|@`g=tGn?nZRbeG};IM zCw^iyt^ShHlVh7ga><0Crb^(NqyQ_zgB2d1sb7*wC)Be&Q|+#7D#etOllVEy#j%Ov@EMShf2Am+`@1CC+p`(Il((IW#34&5x zR~O}(`_+41?97@mr`m27#r{>1lV;BStg^%p6L55xAt)S#prgQA4SUnrP3;sX zr%mjLd~a|5+cII!Fujyad{2#h6!#8~RLu%ZDC(5g17wv|y-@mg)y!yI+7DiWl4~}t zS;jgZ7amDbJFv-2Qpjunj!IuL>eDO9rt6$&Yo!fYiT7im#cn(qa{X60w6^sYDVKdM zwQEe7f6!!#S8pf-mxcKKuVJ`lhJ$#*F=zd<*??mbPZ_~d{+dI^&PdO7R=AQEBRO3u zHMulza&yy5gCM>E8kWmvbT9xTTrE~IfcA}5+vNU(5uL6)G^i-R?E;7!f1BsXolvBX z+q`0#VIRc)rXkOgtsLLYR#%rBk1TDUbN|*4=!_lk9@xrQ^Md?3Qh)8u1+2OJt)E_& z?J(K7P<1YQyfKKXZ{#s~sZeRn%=Wp~TPUfw1-B?uEx_Hkj24v7an?$b=QYEoU34z$C&|SsUileoNjj%_^Gw65+5|ai(9G`zE=JcBm87Wric?Fr7Sgm%J<85BP>ohvSO>g8)mFhW z0Zp9@xImXgmkl7aLw-(XB<0to1Z>2?Mx-5G{F#7*_t7_Dj$k^0;FVJmxn~5Izq(|f z2z~Eh#+vtIzsjkjYSIOzcxP*g3x4W81r9Y^c&t$BUImm-Vu&qWNfW9#a(D!GL+C1> zT4k#e{W2q^T~M=vH>p1o+jAWwZQ1f|J6E5Iz}{$qW2(8`O!m$Efw0aXeSKr4=82O# z3p^}fdQc_3naGK^$qe)Kl%C?dnkw?mr`cMfr$1l@6hX{_i3(FSRS#682Z6(=8dcsIKgyM=pX+Z8qFqN{RukbD*e3Prg5Qzj5ZiLXHdz zHAd(++?G&?biJ45a^25rR~mo6E{*ap#6d!xeu7 z`l^l$xNqn&JC;$6&A&))4j8t)`&+<&Zt!{ftz>+LLfxB=@AOJkBcD<~t{cTnXLY71 zSo_EoTOi(aLJZbghzJWI{APMM%?~G-+&7KdB<+5}aCTmK>e-gwg zNIYJBJ*rE0gnGrF3=B0${rwW~$x}jb?pF3&TU!|ERmnVIwLc$IH1_Y2_R?zf6SRJ| z;#SRWr%#T`x@A76xu(sZ4^O``HiQ_89C*NGrZ6S*tOB&*8uJ4q-%KAfWTekBsit>yHfLs1It$x|_$%2y8Z_YC2^hZydjd@J0dl&LyqI5m& z9!qT}`^DIhEDH@Fr66e7gZrx2?Zz2*K(I6l*+rnBJlwe1Yh^=`QP(sKA*k)}fC3C^ zie*S%%i#ID2fP1FOW}>3o<=x>X_wctxG%wnh7mKEL=-txp~DVDw7VQs>bh@oLrcQr z^1vD?a5lFc$W=sWG4K>rKhiUKUn+jRIBfRlX#_Pm4xstxv~(A0Ym|0$?&TmVi5dv4 zT%@K^2hVntI(wkolPX+@_RYG4vBIp zDQU97^~8BlqrX?it|M&vc*%Ysey}J_Its;97#y#=E!$1J4 z!}h3LmU?3|U;~E{7XQfeBz0;z>3c1A%T+mLTC&64+}t`hRP5`T_g#CvP4C`~0@n=T zdx1jM({2i;CvE4RjqM)DtPd@1tSOiF%zef2tGtzTT}X8ci28^!%b8<~HY8HDyo(Cj zqAl@n^etDyeK(036^t%*Z+u?!+BtvjcyeqOW7}(7?DED&7h(hI58&b&aUk9cw(~$L z=MM-*%pTtBCO6*4IkAuDVFxU4<`Q1*iVV9s|EM|Ko{y3~jokoZM|F#o6H5p>@+M;c z{1Hi>7gRH~v|K&Q5Wd{&)Zcdf1?P6Ku10R4jzSJgLc3|(!#*G89$*gwA&DQZnWZ*mB|U!_omBQp|Ien5prGZME}%73fBP8Ez?XoeV?FMWB1|IUgV_@YhwUpm z?zO8Cl*;lhf5(Z1h59OxGfi~7juCjDiKoQ+?NpeQH&IBfQM}^~?r}xj-}ZQ8U^=Pb z-V3?rRnukK$8H4+|MooV#m+wtBTQSP9z zQenCAxaMVES50nRzLU4))|qT&M^R{BQTtF(@yD{x-{7FWj$um^8C8@r6!s;I@(fz4j)Y+xC93ioEYK#41-S_Q_rt=T@H@saf&&eF`U&NCJN()c@Wn$1Ma6&~9V$ zEP!&glehQUT3Ql#6+IRbU3fb*sV!)COsBg7`~$p~hflU`j4BtB1uw=nN-}j&=E!T4 z^S9Tu5Bne9Fzk$K$ zk)6VKbytp=9&+vgEk;*+?S-ajKK8|=3k@ms5Sw(B`rNs5ftF{##;DuVJR6~N6Gs9kl{?= z6uZgnn>GE}bB-U~8sUiQRa1K!{bqT=5gz)r{Xw-PQ=jdHU;C|uwMJd8kcRQ^Vl`^9 zcZ`p32J?dyZTgiF>2z;$&H7`cbGG=iq0?dg4ySz>*#qvKxH>UXbIt#-G?DMmui>mY z@7&V#qW)bKL(+ygZ{pP;m57i`^;e~8ikr4(rxbWn57j{H$uaP!<##yn5?YS!bI zuJk-G=5W=FOn83*ogw6y=gD2C-=F{&9r&_ z5V_rFDi%Ifl@hY&%()$Ec1-UdTN3*Wdvt{r@@-<#IYi|}cYBoD%E40X*5S53RDI#E z^^8JVG{<*Xbc&6qg z!xPD`b%KA~e!nhWcF=Umx{A-T(O-6$NjasHQP*ul%k0Ng3hUF9Ov_Ql^0h!Bo3Xj| z3D_AfS<>$CpWvHmL6n&h&TlY2*?rY-%YFi(Q;Is-aHl8iTw45GODgeQWAmPuv-0bC zFhV7)f19NVuwp)+k#Uxwf4j{8?H9vQ?ElYyi{lU3N{}A*A1G$*5ctv2x~W;IVU77; DHE$o0 diff --git a/common/src/main/resources/assets/azurelib/textures/entity/marauder.png b/common/src/main/resources/assets/azurelib/textures/entity/marauder.png deleted file mode 100644 index 0d1ba734d375a5c0079e4bbecc73cdc36ea2db8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11573 zcmYkCcRbba|Nmd-ICer-8PP&TC@bTfgG6OzWRFnxDA}BItV%Mxwv;BX)j+tzo(&! zs9a@@-wLF^MqDljggi;_0AOf_s4BpbvzButjhcO97t@jI=(g!Ce|YHSHT7dn@%lRJ zXR=mPM|rq^3vWQ*(~0y$@*k%D1uHmDnO&!b`>!cHA^nvj2~l2fn~#WE6JOh@Y{!4b zQ;jWiS!n*SCJvD7w2zQz<*7goKDwk}hiq`qS+_mt3RMD^*Nko->3NR6?0049QLb zKi*(7{0X}xm#(}!J*H%F?lTW`ywbHi;E=A|OfBGkz-v*udq( zaGo=<30#==Or}Rv|FY>W?&7Zajh?U58;%+ICEX9MIc6eN1F6jyZu0b&%jdHgXH}t_ zT5rt9lzNTF!w;D*%J2tHR6G#eI`SMMN2>OJ@c#@55}IRltL*tdw2m_J|JpAb++Om! zyRkQJ54kR&L7<-v8YNdhLW)7V`QcIp(YVH1rCEx!;9hLwXY9syIV$K)8$=pz>+wS$x{&?3$x%G- zGQnk^V#WwDgi5-=4F6qkD(f_TJ7%Y*CAx+f=d-KVx9= z%nNr+xKdkh?KiLuQjSJw0r^Z|b6vkFo;77-{6718}1kcAwx1Moo ziV}h}zgK4W_36L+G&IC;hig^0vDh>F($7WpZ8~NYWsf?0xZSp~_4*d7_N<=Z=+9v# zBW_!~3yZ-nh+uixTgnm$N&Ja`#N~&0+AWC0Xt9 z!=uH7O0-c#@kg>P!Fol+22QL>@ADnDD2ly+{?#F;U7zp-6>=4E6Gx2Nq1-!D(Nk3b z&yThw5>WGsWY+)lY#|_27%ru^kUit)OjQ7A{lWn5#-RdNHKD=l=EWr6@35nQpuE z$W+YSU7eT|5WS(^QFm}xn{;$&;t-YkoN`$3>A5cTru!&F)qP`mR?dk`4xW+XzcC(9 z-7|?2*mLMq4sq&OoA`ws7U=|V;g_YLs+ZYng&moLn4pFR)DKrE`CJeq!_Jm61nQI? z44-1addV02Yk%Ms#wwHF-(Hl{Q0yfw6145NGtOV}SEdinBa7HHXOBH)hwS`D@LI;5 zX@j}o%FCE?hH2*4wjp=<=(Q4mPGrqY2GFy`TT-q-YTM2yXBBC0`B(l~8GDjI+cd*_ zNkBvjC~NH+CgyC`9c+6QEs@&n&rHXuW9(kBi*nsVs#z92e$Q-~O|qXQE}7Kl5}(Wz4+M1q(^W@wA#I$R?`*OUNUC0CJJ(z zPr>EQhKLFpOsuXCwJwL2$4&swK}|Vbd^c+8DU|$X+4~@~OnjA567J^4(z4>P# zHa%zUMoLYw%H~R~(fM*2^)O=c4KGXI_=Gr}$`phO4qXX9YT$WlJ~6wV?G`hxz4bc@ z;@&J21>kThBiNOJ6_eE@AhS9n=*Uu{{&nnR8RVR~W5<7mq}&}5w~;G~Vj41tIHIxk zsfq zSa;L(`TJM<8-{S*1&5a8*gO3Tmm+R(&7AWMF*Dm=^VNY(p%!2Ra7dfxnc-f)2t;Vi zEN_&PVswqLZlibnbB~zyp(@;su2f5OYz32^^!4Hr|D748sf^@P@$*!=!n}8so0`9k zW_v1fholU@o-bVS=dvVkMJWMk%ge@QWN}9K@>8@V)NkeKxJo1jS=|N*3=oe z+f&O87#-386}DJjw&d%B<{N$YdkxL1O@gW6CIFbk!apD(!C-mzdt_ZD`+!Q=^%%-w z96}#;Nkt7a3sp{5odL_6cC`19+*vfQBn+1FjgwPT2KFsLQeMefsdD5C4E||94moZ2^QQ6a@8eZ((?73|HW{!jLZyg(9U}X=awqS%aIoA zM}H?^SFRRF`nyAc)s93iBjnwEMJ~qkTa=mMfK@A+f}Zp$y4}w2&FrKD=ZfaRLU_0b z+;K)x;#zAJo)n#B{_NcomZHR1gGMt0w0A!W2lqRh=%El3Tg5y7@W+*DDF#({&wlbv z)JGDt(Uxym&$vB%Mvvkn#*F|-j#!9o1QA9PfD?o@P!@MnB)p9zd5dI!KfZx0BBWVq8X zPu(~meoq7*bA6LFv)-Y;dlBjc39}9s284p83{L#tFnBe7rde2&~R?Zl{p8I=BNa{+%q{l9OYXi6HK6Z-9crnd$`$9DP#dL0NfIh;?m7HYF~*eP_sERIN)1I)<~%! zj8S?jKK%6@IPtjazIAhr5#{3nu_PV1-p0+3wg`mdiN%T7O&O}KhehA&h9Fml|18A;%33Y)_QRE z{L0>|VKK;$x0%U+!Ot$$Di}j{m;tX)CKFmzJp8-9-dU#K%&bAY8@&a4iNTXTzA97S z944Q&m<@jr+cGz1hXN-oi(9(Zs;1}i3DD=&A+~D&3XqOoKh^I9Cgrb2EF*6`E%FG9 zihggS%7LKtd5xj1{2VuWbv+-G`Ip}r0YLX_tXg%zHh5fr20}1{wA0l)P?PTTp5>F(gOmmDlNRq=tpLeeR1Wv-Kv2pq z9w>mK#h4l_yo@XQLdD*^#~dOhr0_gG?7MSBrXE{g8@@f2*v zhoI1Ml*k%3qY3epCeIXR#Z8w=-JKn=&Ek+81;}=TRB-ntZsa%m`qOMG%n;BN*9|EKD>Qj1 zYRTs5_I5jlh|uI$u|j8cg*cg#!k6I2|EG7}PXOwMBq02f`hS z)hb9GS@9$24F;oSHB$6&?pp3bk1#4D*(8C;$N=hCW^AiQ?UQ{?In>dz75Q+mK|avu zG|^f<%H@YR`L8u4w#f20q`<`0P%*lxbn%l>dse*sO?g{t?(Cw80%|ArJ4UahsuiC^hOBzFcIyoloYhBWGtAEX>i+f zyM2+jUuzv~MstcT7=0?&u^~<{yA1)Ga!r1J}4)_u9)UPxA!WZ0a}?j!B>-SYp5mhw;%oTS zcx~WazL_9D)Aq)0ZNcTK$U$uuaR0xn*)rAX7=x{*JB~AWR5)CKF!bxQ)Aj^aml;3| zo4!mK?VA`kjYh+DN;>}L^l?n&>c+MIO8F-l0DG^C`le1BJVM&hHkB(;er{=Z z=IR)+iR~x=sE?GJSqBSrRfhmSz69{@Y#G~g>*K0r4Z4q{PBPQID=jJ3ZW!HqQFD}O z@4t!)rUW7O@(I#eq=|Q^dwU1;#bkROVow0|(quWgg6BrJN2L^N=je*Y!S=;3m(yZ2 zW(|y#TP~~XaveH12{f;4s4=b^2o68L8f~=iC!!)(o#El)RQbv%*&*zoa+3B6&M8;& zRuuG-1BSQLxS0y{d{-)S(-k=^_Uqgv)PK|uEn>y%QOE%G=Gn1LrR7mh+B7#>ti+W)MaCC*;OKqBNUW#f_13*-&h%+6-?r40mD7yud%qG&+v|` zsF9tOZNBnqVYzD~7?ir*Gv__#wh7&i-t$P7jj;2* zHxduBXD4|ff1r5gw-GNJfN9>E{3N_}H7gd2ot3G>7(*{=C7s|fBKm|!Wq7VSkZg^7Z7!!t@Xf*oaU5OZUCtke8ESF z+KRbiXjGJL4!oTEZFjrrZM$|yY_tdu{gZ5y-sK!RH=RYnCNTV@AD)eDzkUta&uYCa z|79cs0VKSaep;16Ma{s2P>qpYnFFbdpF{i4$NxP-a;P;LI76kn)UdfAfuVOqKeU1x9s-YXLVhMUL`H z=wrPo_|5ONP$EF9!giPU+MLmZ)*8!BbROgIF%3Fza0G7a#K~AXx)&NW<)@DyXEyfr z2Z3p4_k0!}N1y~gvX%pZ(=k1J5wn{^D|z_7ilcs+06NSxM=CsP?W(LR4<`Qsq6+3$ zw5m~kupL71k(^09); zZ81uGV8K{v;IgE)87*Q?KKW7DB3{ZQCLQtgvB?T+FKJ$}`sl)1`@&kVQ=QM`5q&Hy zg?Hui`*viv@S$rM4XePKItXBPdT{lRI~aQH3Uo|d|Cf_~C!jPecdEZzM^C@_4V|8( z8g412$(@R}S;G=S*N^Zp6Pt&^UdoKihBgqkQH9OzV~aoS7|YYIq4uBS;sCm@*4C&)>E8zzp9142`@CbX z)63f64?6EoS}&9VT`l`G=JFpuE*ZKEVT&+Fu~14Qs_63<#tJuyDtO-3l0PYYS5Ee9 z>qvE#I_3&Fe}sKf&8_;>F|h$@R+ETq@YjmSKGjdFArvYE@N4n}oKAdVPYK$Y&pXg@ zi)fc&!|_I16O>FjtBLXJt%g{e=d+@;^$#c)E*|0~B_Gk~t)9efe9znqUh5Ft^PaRA zKE(UoiB}#}2dqiph14$PTqv1!UVe9(-`B{ger^P_S=7a=e9^GWwz5 zroaQ-T{_5HuIX>oKn_jDZLlPou}xFLG}wv@w5}eiK>}+AzG^=Re;*U>W$rFd_)mIe z-nwr<_T!rZyRNZE0bIyylHX~2`{gkCeyU#VP0diCNoq$odIUc^B+IWbmMUA7UZp#- z9;p}eW4pG7?N#3@PXF<%B)K!-IMCglIsG#&aFJVbW#ktAm7V`@}Xt}h* zQrUT&H5h;DsxbvXL0rCaLzLj^2b2s0+nL&J^(^+HlTAN&-*179z4eA= zVZDA}f$3GCo+6an#Q|9YfQl+X)cFv5XWZy$&Dks6)4R0R`XX;1>^e_3sGA4EU0k;i z8kQDXZ5$&YEOeTe#vS*4VvhP8e>6f!5=OAvFT;hY4)abkJy0jbb+Db=F}5!uBwXfa>Rw&JS#rQk^ zSWi^YgDC(I0d3;CubVs~ry%xdq0`{VzPH z)6K0A^*JAprZ$s^72WOKk5=XMPK+=E25t3cI-7n0-nV4dv+fDBxZ}IiRGDdwg>;NVxPUfEcnA74n*N(_ISUH$%UA zrhinsT+cMqeBNDQCl0i9EJyr;FaUsLWX#8?Fc*WYQ1=i`u;3@}j{GdCj1b})v_8u; z){*3v=q;kNNz;O)8|Ej6kDf9BK zNP*M_gcm77x9|3!-G%dg$--n3>Ci16?%=BQ*!o`4Ge=^q&R7&?Jyuwos=fl%jOr#7 zFOZDgw0BtuXDw!3{`AOrRvoP;9z3p^G~`Q@G`=pTeNiz>ep*UOi5n^EcFaUAV)HE@ zxG|eGA~9+Of{X)d8i5t%EsE}LWr8?&XV1^Pn(HC)VHcddE_BkEvW|JwTX9Uy<~ndg z#LxV)sopS_K_Mj$`v*)w`pk4nO!zDGIAOQM39T;i^@dwKvpp5o`tiHUk54$JTZ#0I z8+ns+^g#O2W;_seMo8tJ_u*i`(en8)h4l)sKVK{As;tD(ohe{uwsqW)PAbat@uvBj z_^YLUQ!oFa1fbP-fZ8nhzIEiztDycdRp?%*xpIibq(jLCDWHwE0c){^cU{6SOf2+( zRE^_i+HjV70HXA2u}Q)cJfD*k!~oIjJ}tcHzgfMkV7-9#9U=QoAsX|Bl^GHc35iKR zDs1qsWA*H~)s@>G9un|iVs1Zd1?&WOhwhSpV|UGnF)=gIawqLNbJyF~tygJ*&*r|0 zQ+~6Opd?dsqXfj+$mIN9dK${jP`;m`-zp5IaxDVH;nn>WGr_uW55xNkqXWV+ZTR5Q@CZe)2jdRC#MW? zm|m-A;o$c`+H22og!z8Gwkh4{ltaD{?%tF0Qa~t^6W#}Dg~yl6d8=$}ptT>rW&sC* z+e8jxOu&t+sNV#_3p+ejpg2WqDEW+lK(n~axz{lyY9{n7v%s1(M-hFZfRIsBe0pRoqV*o>4 z<3b`@6pmz*928_Zb1FjVdn4{fjyFV!BPR3sOIhcWGSn(9ZG>Vf4wc{&A!-#9VRbeu@5W8(J&LOWFufDuuyisAXgq{BuQYD9-{JQdwqp*@x3e67L!U1RN`f<_m3 z%ASjkxJh0=kX`2FN<94j*8ar%<&$}MS*J^R_)6%pRVslc!7m0pgDooVy(MjDg%%|` zZXa)mxBB}8D_DJst`HKkf&*2$pq`<;K-(;%IWykx32%S@JIQA?OjI}X8&icAVK*Fl z&h)Ss7nJ|b9|_O*Yl**%zT)j#!i>&K1Zp1Z*tHauVi^^%Zc@|tzkM(wh62Ee+2gwa zq?6F}E1QwADEJ#O%x{eOq91qDqMtM&spP{hw`c1;H?pem@Ye9(2`;w-aJDwwL+5mK zk?h;BasAJ4bIr=xw9AWLvpW}ql<|a2L(`eDM}s~=E!*3dfF_WdW!M_QT&U#RT02M9 zvp4ga*qpXE{aWj65`sT0Jy7(>3_Z*By~B!$OP>SE0S@X@0D zT9C2_R?;e#p5ZU8j@0@8fG0#x8ZE*M=yOAvzS4ANFK}ZV(Bz9CIn8?CzXGNa+GF6S z;0vl=j)nM8LiF>)+u#hL{rJQ{$sEPgapU%8>h8`iE6i{OS)$)AgN`xZ4jP7Ijg|gv z`8~9nY2XZxh&Hd(X^gzx<2*b4>*|elo~Ka-*;_!(ziHrF|1d0pqnlUV2QO&cgVHyu}`J79l`;uAKnw3$j<%CHH?dUoL+q(VvFm9cr z$83lom&BY=FWzOo5W{olUg>I{I>u4so4^EblO?Pzg@pTW$Kj`hPeo@oRGa}k_c{e1-*(m<0CDm$$9lndWa~kmz4cz|&`NseEoK<~CHQOX zMR7HsckMI*xEFQrgaZSK4ZW#8Iu9usdm$N9yxWa$XfY(pQZW6=?hfe)=;JGi?p1D` zw_T3QL)}NakxeqI+m@eWPE)M-Rc?uEWqG2pcaKW#N2d-SaaBKcx+F_^%AkwWy1i8> z106F@RpAZ?lY|fZ*$VEERJ!!b`>_N;m8i(Hy1J$G`VU!-{|5$Sw(g9emk|?b!iIxN zK`jaA{d}Afz5o2UqEuF=_yt_RN=f!?o_fVtxCTd)rt{QIyhJl!DZs{m*$`VY9jl5K z?9mV@-`oh~Dc_Va7EfC^lX%wPG6#a^@28u&M#G&LUKs>;+AYu!vvbo70i=e5{<1X< zn=H<*LsT5?e>_YTsz2Bw_sqwHQ`8u|wgUF%oi4}^Dfg_wH@K7hDr?t?Ty*z}1U!z9 zD31L*ok=ohz&2j5IfOCyx`B>D6uaK$jF1)fIKOhv@x= zJwSnC?47T7z?A@%`}1#E;;gm$sNt#ey2QZ7n}Wl^6a&V2nrnypVh0N|a@HMoEOK*! zysBs8+%X`*ymS|=Mc^Y&@1DTs*C4QJ$~+6a`1`|Gz|nI;f|E{(ik>uhOO;4w_d+{e z_%EzfL{&ibVt*^^8xJ?LxQpGMiwD;&u@;LbUL+PXPPNbu`D(Tk%T7;bXWvX{iACgR z#Om?>!^doQ*}&3>QIiD*W8}P}5?nE4J+VG&sL-6?eYqqD6|KT-QCJZ!q#!SU^p+Bh?jChc?ka_NS zB6i^@m5qQGV&MP(KDn9QKnythq?>8_8|1u3nThR->=?y#w3%hrG zpT!#p|8@I63?Hf&3a;JrDXW;cx2@q}qeHyh-p;h_D^>5%5e(IJOpn7|v z7WULH0(nkx;tJ(Smvs=>NSF%%Rx511vJq0r-9Uc}wj?_I%nim_WBo$9i2I7U7wIy>_0V!XZiwgLV)y$N z-d3E|Bx%IZP!0cYwa+DX7h0KwqySYNq38il4y2wd(uKLx!?y(dw&fsmx(VsvmQS1U zIm8F!*f?Hls9sRqF5(|aP!VxTV$`aDzx)Pxooi|_pWzi$z~pI)>x+Kn_sXT zlN)M*B7I2;@PZ9~rN@9%U&&E#d;>1arRd1A>?E2eWE?LoaYj}r>f|ek-kblFhRV0M z;KeNSzU0+f0%~FyYp8j$2rn3Kxi=fF5%aKg1;rWq2rv~zpg>{rP-kOY5##dJP_Ahn z^)H!2p*TY@Bsu&}!Ppm469Jlu`5H;u)8wVw&$z#k!Ny84j-V zI2@t|%MD$q6cLAlTd>#iO&2ep7BOtIy(SK1Su$*e62MCddz_MO44zMqvY+&CPuA|% z{Mq5yDmuW`xN}88s%_vS5WN59#EbP|`X0NJ-D!%s(O0irDK8-;KM(5BT?T$CgAzbY zrsM40Ze^z_>GZ1v#fN5kF(v(oET|Ps_++e0k>J&l)EUojfZyl3{Y%SW)R{>;-O2OA6Kmm``78 zmf>bZpDNxGUp1Qe3|y(Hg#Ir<{|zrVD2&DL$I5u2*KXoNh5VWd)iIKT>_XM*nAyR% zHVU!U+SqSZ?LDUV@fQ7XKk*QUfjIYX3JrqiIM<_gcE{LVS&K{7 zt;Uju17%F1KGaY8ir3z4iPa2ZzesMluxFedGrUJgFp3i&O9ZZYHlQnP63+cgWljk%LPc3G zh4GG?r4$`sDo}Ii2(4s@+tq5pGI{rza8W>Sn%*%w=VI&i*Q2-)`qtB@6lU@QAEn$O z=vyy%cq8rIp`iTbbLthP9ItrH1jg-RlR^$cb2m5(+IyVx3J1jj!Jz4tgQ((aRYCge z1lcOdYJr{^Q+o1Y5Cv7n`&Sv??=aOxTF3zpWO+@H;dl^lun7Y}{#wVMOrZc0{a<*8 z>*!evJIMT93`U<(awqu^_S5(hl44H(m{I7181%|=X!Ca9#cT{X9Dt?f`#}quM2wN( zUC8`ueEAR6P%WynjRa`;!)3J5M$bYh{L9}PkVXd2&+OAS?2>6c)e?ngm{dci%gx&P zO&C5jVoZ=Jjf^D#=Mb5x8QMfct)8~dlY&Hyu=KL@6H|>R)>Zx)@=e6&9gc*@xl9LO t*f$vvA8V26w{BYUO&N9(wj_wFBV3QZ8Sld6>by{^ea;J@k|8c2Tdfr zbz1|a15<=MR~d5t7Uw{)beb|+EniZJOy4&5CEsHhUyOQhiLGB_wchU{mLPci^?Ye2 zLo)koQdgNjDM!;pXz|iVGR4^A2yK#T(rg^-F3%i(kXxVAj2_o1s_}Z#x-pSy2kCkV z?z0$H;$2VLSvY7LzgB22nPZl>t8Vk%YjY+^&M>Mgbv+s;Glb+ zPeT8)KebjOC5-hd-9%cncDSsZ+Yr20BQ)!?wlUh*Ql3GQ({j+97qi^pwrD+`l7sO! z({J-{{=k^Xd3c?%dyCiIZ)g9d8%gJ!3dGT=aLpHKLr(XiKVtA~p)sklRkx%F4Hhq@ zezjjVCd?RsW`(ijtywi29@RAZJT#FhJ1EXA7!M_0YCn7u>U-4o}`hmCLC z(}I|Mk%=Rc1xHtbcM%nb6QZGlm%5Q;(>kT?*0mLr+? z*S21t=We^sU|PQPsI9KE-QUZKqv0?2`L(6NP;|_SvIlao6niJMZ`0^sfU3k4 zSXC%<*$pU=Q0oSDVGqUj4cCQ|B%!e@FMt2SuKy7;D}T8r zg%7RR+?$tNN3))28}g4D@yQBami}oCYAKlDxvkU#W{7>o6>@gIc!FCQ8(iF;$53K* zyw$uZ+N=?grdAtVDuDkq($WK~o69@smtg9|wr-Wzh9gNn?NDiA5RbejnCF$5)K1u! z0g1Lpx+h{KY&;pcc;+A?zw~zSYL7=C^p6zIdSof@X$sus#4A!P;VHZ88|@x=_iwKK zj00dlML2D6D{m9ttjW6O!cp>g$vcsTfo~EMb|Nhtd!2oqFY+lmK!Y*`7$n7R3z$DR@2U&Wvw;RU9TmrSe&j~YxH zJ}t0mU}VA$14X?tmTc%|0VTT9Cf z<>^^m|72*eVQHkQK09dWd|_47)e9MndIwanX5|-2;3lr4n85IAT46ns#Pz|?B>s9Z zG5rLQb(BX+B!`9}CytMM^sRl}Jk2Q zy=?5)zbSbXzHbh0^aXM-{m) zTmEQ!uC=o-&WDZTFN=qQ*XqOoOq%I zx`#R8N(xqoHqUeBK|sI>71(S(9!m*E%n(g80&>e}OnG;{luE9cka?R_%li}H9Y?Cw zQ`VhquPLx>;YYWNU2>8K1odWk`mgb1KDX#(y}(&tt!e#cs^c%X>Cdum?O=3yi>RkltGZ3?ND{V`VC&eWJw-r|6eH92M zY3-U%{R!sfM%6Kw&ugOI<|goa?Ydd+i>n(AQ2VIklR2K$X^$HCJ5dZ3N|(sEChisF z1di_gFfgFBr{8CR#$%#ue#A-AwxMX-s9^TO4iXNIJYkng=u~+M_Xp`mLC(A7wM}*wAK(D0y#XZte1;{Ih#?+}_yW$Bk#5R*>AQ#-NH|DPLxtQwj z?bZ_-cvY~_C)oTbAo!&H4Y^!^YpW^(24EhvtHuXVBI>uY#=?s8RDqZR_%tr{m+^jx zg@6w5QynOkzd0)C)*cJ(==jmxx?Kfr)|i5$r&8ltv*zGv*4~fE>Y_C^R;1tK%0x7e zo*MZjzDe~=hrH$j!tpHSRZ3@a`uowZh!groBYaBEg*W-?`wn~ZKZ0KPvlG(TJf@`O zwDM~w500Sd_sCnh*1pHXC5z^MLut)JK5O;ytHaA5r$DwB1p7!8Dw~ryo5>a)R`j<< zpouiTG`YOavjiUl>>OX>Iqdk+B|0yz;x`Qt5<%)o^*02cYlyqOe2z*VMG@n41xH}i zapBs_uwllEnc)D#BsfS-W#W0&uzXHsJhOLr727bB6|Co-J?qrM9a%6QVn zA;b6%7>+Xp5CNAn>QiM$9++lgY`c)z(30>79a;T_5Qs+_yJYvqjQ^wa!ekrv3_UZEe%wLOz zS{>4J8$LiXr)jMO9Kdf37v$#v3`)2DylveEUpW%9Qo$>u=X3Wu6kKQ{+<;Tkm?{aF;T>x3(qx3Nj$j;11@u1Gbk zIBvm)$P3)!F8QLA%)}IO+SeaH8aC%HyK%}~0#tQgr7$Cpqgo`@itGVPfG&G&JQh|y_Uv{3|R%%A>m6b#QUiLDdcCnn_PW{A(3TNUVT zaBpRnc8fvlMwwc$a-KfY$yll$Yck@2?F-Xy4d1>ym}Y&*yOv>j3E=1IlKB)oa2_(G zkw2UD8r`+77y#5$@c~YJtL4#yug3Qp!X0MkE{hmwXvV^a5`hPH> z55%d{;T8Y!;u)#Q{S><^M%nrsc@~P`*2QORb7WHDmq$juc9e)%J8IkB$jQ_GuUe!Z zi5}e-qoPR6?A|)~wf>UBo+M=@MeQ6IU@Qr`%ffw)>nn_cENX2(DdJ{SWByO zf9jl3RLYtTS=?x0AEvu7{E>+XD;Qqjt61!HGd%A=Cnz#foU}&Yunz`M6Wl)##hR*_bRF5{od5j6jvrJTnXIo$preIULzt zUvy-(NWO<#ZImkOb|VBEaF#nJwbtV;{;5<^y}NkG&|BKNu^o{AlbflJnht3W`lWF*HB#-VHM~-5*U7ZhA%4+e6K#h65JU)#b2}=M`{}VAp zBeFSyj*O(J)nlXdm3@wJSHQ)}A7JHj8sYw1_mUh|@db&@PX%H|VQCIgUI8D| zCPQK?(^snR1u*Ju7#F^LLC zFj~Z>gvW^Vk>@84sxCQ4&Y$}hUVGFp996VU=&+#S5sY!VIMnv0^d?xcCm6l+U8<9c zYWL>a4rCi$p3*DJK}&5I@021P^`Mx!d-Z9t0|hQ_y8D5OxJ;Pu_*n&isT5EPp3s|L z;htRZ4AKEQ;HqBin;a2I)@Zs8A5EpN8Sjr~In-Z+RP!EB!2gIa zGRutfOjL-EUH#a2p$Su4{jqE7^lOw}|Q&uPiUlFLu})!QlX35w8;QSO+~+ zPYm1Ym9hc=`S8tcD^5X4@H|uuy#W9MiiZacke>M%CB*SjeXWSIOGt@MB22pH{T$U^ zD^*pH)d^TQTsC*mnWyhOU9C$JQsS4ouHJi3*`Qj2hg*o~vRd7bTZno;K~3F#)EaGp z#aQsjk9y%xPdH~ZZ!J?JLE-!-t66FSutTguWVi*@i{(Pc1;co$5{I(5iUyJt!L;)X z(W|A5o6wy*v`O!sv8>CsC0m}g7vS0-~3kTM3^gid!9o1Amc!r^7dqVK@M{;KQseJdP%f6RGzHy8W` zIiU4Dwi?wcyqsRAQ{_=SiA>68mS1y@`yliiE8~Ogl6nM!0RRAEpiaX`5aZ-UMo#`J z@J6L!V(j4O;;k<`~_mV+*vm`2Ei3w$R~O-0SV@ zEx5Jc-RSS6WA_Fj@_*`R0Q7rLs_JeIs&-(b0siLx!$dXdac0C`5#)Egc-Y zHZV-{RQC3-3(B`l|Ev_d&y*2&t~B-4c$>Dv{M$%m;Gajr%0-XGsSn`7l?{uD+4G0% z1N!;Mh~NziQzSfU)r2VDtfThSq6Q{jMtp(@yt_5Pka=i~{`^V|-4CG0Yhm(*Hy?w4 zZ^um(-<^@Q-zt=&aHh3(V<>pObqU1{!> zeo)$=t0v{--PJ_qsm(gc1-eb*K&J+N%`u|1tqedy95s~~BEnaq?LM5;P%%_Usdq(i87VJxPQ;v+%37QWXZ23E9U|-Vkl}QLAMXu<(@o{6M zx#V2P2*`9i=5Bk$mmYG_QM0paK8Eaesun%>)2-gwD*bhN8icf|%K_U|lb!rVT$v(g ztH&1UJDaWVkxKtkDTNqn(!u#{HmRdp@+OyeHYOE53cjssAso3}=09FVw(Xo(mt4V) z|2I15X&~M3BSYN{Y4PponLBbYq^j3GwF#bA*aUC$jth$(T#rP2H|ebwdPDx8vRQo%ZhCoScAwF};ebgA|#Z$2)+tO#-HYjMm6 zw`-|lcRotGb3(8c?wnklGBEDq=gWCeOlI8Ot|E-hZ%9BNk%woR=L1b=mi{3HZ9bvM zl#x_1H-z_vQB?8>O2a>ozp@AT^d85~yk-Tu*-F#jv@G;T?}$X=!ev{>g)RU-iakdz z2pV+Z%S1&xTEL6Ex#AkM4s@T$H~ai*vhL>9KBkFC>cK5}Na#PtU3iAE z7C)okh`lykynKlZJsm-l6gEFqSvVDK?(cx5p-BFJ&!RQxxPgi3tjZdPf{s5BN*h zWLLi`={P<)P}#T>qLyQ@cp-?#p*yHz(!#^Ult{8vFF?k8=bc3J-f^$yQdpA{^S``5 zN(gbTYyG9BKiZN#WnYiVSxyQIj{BQ=vetyZDN=#}nGLLr@rA1)X$ecXc*nH=?1DM; zmbbJJ`L}u$+?9QPZn}E16f^jr6oMol9+1>#!7ZHx0qujUtw$iL&a=z2@@+6#124H| z^RmCh(H#z;Hz|-mWB&u*gC`fXq7wctJELoeGwrqRwO0+kP+d}4d6aiopJD$fb>k-6 zu4M7nF&dDb!i8*1A=To1cL2>Cyg2LPs`git&G%mHX)7!o3~ahuPjj)jA^c3@gC3ll7#yX4BCW05M}fr zpiys>t@5M&iZsnaxi$&TWts6R?DN#lVe>DGLMYyUSZX2}eRUtv=nbn<2_Bz$M&=2n zF%a^SBqa+B1~w-^+=(EtR^QFzWB9%7L&m=_7!c2K;dqjkaTyP{ z)>Eiv4%TPwbP*m9yuf}LySvN$`syLNQwcy%&SF{q9^6@mmoemdyb82&RlpkbTZq|8`BNio7Xj`xtu;si8 z*pYa(;dVv8Yg|7tk@o(-A$u_hNWD8YO%fv$16Vw(LAko_sIxg#l41-FU#+>CG2cxo zD~Lc9I$8w3Tj8L^b}o8p0H87%ZOs^E%>Vu58Wr6&84&+B<@kQ*K6EIPB6zrbyS&ANU42PV{ za}r}_+qwUGKcbN)dZwaq=8doeYQ^p5JV1M$N>rju1$p3ABAwtnb4h;GsK7`>nxQ>= zj`}_7nmWxf*F302EOAYSw(&7y0aPU4QkW|ghzF@=+w@lui_G~-NE?bfNFW($e1KYxAKD(on-kvgBUBwV+uR;nJYx}L9sNi{ zN|r{b4?UQBfK>W5kpIaD*WbZjoe&_O2vC6kH**Ar6%n@jA|a;2mts=d$vsA5vj?EL zwDjdPxTsoms8=U(go_K=9K?%kroojM-Nr%hs@l*lmxZK-JcsT<1u!~kQI1_4^t+7e zyPAvRTJ@Husru5>RW8?}=Y9O%S;q5pFu%Jg6|HlJw$xqvJ6tRjOQry4wGghadk8c3h7i2iKHt=ZvCI zLX?OAs0!aSqO2XqM{gMTsw971} z8t(qUM(aD2clLWXKZfvVuI3!^yM(&RSdPOYu2v#6S>sHCT-r>v%(rlXyrpq#p) zLF;6smXnW>jfIblhK`AXjE9AchK8PsKZb^ehJ=2DgM-yhj%<2zcyejLHg~c)a~(mFi%E1B~?ixNkCF4CL<#wB_bUg4+8)I0Q8KfrT_o{ z0d!JMQvg8b*k%9#1G-5>K~#9!wbqGt+Bg)(@jI#uw;eRhSF?4~AW!1N*^~EwxPH3E zAPeZ;-*MR#{VV%m9i-|>Q&&_s+Z{Plb!{b5jx+G`*WV0OJKOE$&E@5BfJ1F(<3ghV z<$;?=x;Yi-+B7caX#9Hu?>_uu;KRF9fq&#t~oyZE4)UPv{WOsa`Ue+~F-_TLMstHin4%u#x*lDe);EfY%P)xQ;wVLXM}ZXcY@W-_Dx zsme_fet3WX`qk@K_xIVjeJ?EFIB~#&I}y0QyEb?C!?>+r2)I1pjsni*0p~_5*lf02 zJ9#m7DYo0qURCYJudjjYYwgGTfUUs$tE;UeD{z?OS64oBd&_YGxx+XoGvMXp$14`d z3ArutgO8b87RYUZ?P$EJ_#g=YeM_=~C!r@d-;o_vg`5J!_-la}!mkBF`1ute(GU{_ z6y}Ls?v~FS{k{N|fwisy zK74lo#DdKSt@{<64}?M)sH*-^&S}D4lZ*m9Di+d%d`m;}Er0})pQWnj18?b%2jnY( zTBw!5G99K7y|~cyq!vg5AbU`fUbmmGOCEio(Ti63xYK_oI2V9$(^hAcFZsuz?ZOZM zED%B#fPHK_AO)c2KLrEQgsu&ZpeIQPwd}zh-gwyn&MlYD0UPn}rD?lH-b*IbSwIu# zH0iwe!hg%9A0E@pu9Z=MvVbSYgr`o(3O)pH^9gMS5hpq4#&=@~(TK$1BofH%RLH+%@OA)jbkf%={TIs`&ZL*V>=paAll%V}}WC~m4!jNb}C zj{^CG5aO=|VhBHW3U#^72c3hIhfA~vtlbch)C3OKjUXbe%})XQ-OvYgXHETlbu*nQ zq!|YwZD#@1`6>Z04gh_?3W^Y#d{>~hQ?nY30`vLY%TOR3ZWz$%K#t4upNuoUuES_Q z@V@PQ0hyJ^%I2s0fmXGwM*D$?wB_#wIfrwcJFy>>zZayiNCZg=pxh5irvp&c-E}|O z557A<7AW_Fk`&S(*TZeimFN3GV~XDk$AMrQo?+<=KC8OJ9@m3G00000NkvXXu0mjf DDoZ!s diff --git a/common/src/main/resources/assets/azurelib/textures/item/doomicorn_boots.png b/common/src/main/resources/assets/azurelib/textures/item/doomicorn_boots.png deleted file mode 100644 index ccb401b3b7c07bad70354a4b77649c2b840ccdc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9JOMr-u0ZL4Lsu|2a5La|ZYV`Rbl7jv*Ddk{y_PBxmvQ^cb!(P;wEOnZPT^n;-xr8in|J c6f=G?s@`Q!ymNKoe4rKvPgg&ebxsLQ0Q|5bX8-^I diff --git a/common/src/main/resources/assets/azurelib/textures/item/doomicorn_chestplate.png b/common/src/main/resources/assets/azurelib/textures/item/doomicorn_chestplate.png deleted file mode 100644 index 45562d9718955c8fbd368a18f64dbc719074afb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 338 zcmV-Y0j>UtP)F1LIeVqV_tpJ<<|Nrgn>!kpXp#Y5h{QRB(g7ozA%FMu$1bOlD@Rb01q5z56*wFd; z__M8g3g+uYRm_x6wmaOCCS_xSeo^z)PedYS=! z^YikGiG`sMeaXqhq@m?B kYkHijVXVdqcf)|ECc-EPMph;4;Q#;t07*qoM6N<$f;p_C3IG5A diff --git a/common/src/main/resources/assets/azurelib/textures/item/doomicorn_helmet.png b/common/src/main/resources/assets/azurelib/textures/item/doomicorn_helmet.png deleted file mode 100644 index c969da1462109167051b4cf6bbb7a95d7f03d85a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 503 zcmVAcnK_+8dYQH}NJC5uGH6INpbI~Pm!)DzFfne8#`uvm!Ika|>Pk26L|j;d zX@ay?TCvcfH=&)GV{D8G(7Lek?4RFx^Iil)J~3pT{{uXE`bd?Edwkt0@MFIP2&B}R zn?KF%TlWrqdaQzHD-YFfd5iq}RXigEQliNaBhdu56~{{1+_--CPwo(~^5U_ob()k5 z>+F1dOW2Gf!(mVYO*iPqVvL>0GHGYIa%riTJpwGNcRL$wyA>upL8nn8@IAbCi^jJK zZD*P(XO8*AUw~Ji*Vu5&Oe8Io@1cT#An@@I4(N`K;8+>Xr=|v1@TQdGLv@po9wHKn zAf@b0S_pv`_&CvVE?B1q1MBV=a@(6!>n>)(#4rq$QV0YF8q@JKv&qb0pt@V8P_Ix_ z`zWEI34ubP0VfHQjg4a^(_FoLvwsuwtFQU)mWj@sBk)a>FA=(jCiiINb0i`uGG{Nc zaP3Zip!8{tx?5w^c4#)B+X1ZyH0inpVrHDN$yt^b9~=jMHS77yWz}kWl#2D=o7nD6 tTh|3MXVTbqjKzif{TJz94r}OR`U#`sxe@0ys=)vN002ovPDHLkV1o16=w|=` diff --git a/common/src/main/resources/assets/azurelib/textures/item/doomicorn_leggings.png b/common/src/main/resources/assets/azurelib/textures/item/doomicorn_leggings.png deleted file mode 100644 index 5d252cff21c34684e7b62c949585c39b804dd715..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`L7py-Ar-f-2D&mHFc9#zRucXg z@INjs$>)o-F>BrN_UnoU4gxHqVh<|g^xgCP=k-mrXsj0zn0tJ}ay5m_d0G#4)m46D mdzrq!K(1-YS`i+bRw>C?{(lv_eYOHkXYh3Ob6Mw<&;$U>X)TQa diff --git a/common/src/main/resources/assets/azurelib/textures/item/pistol.png b/common/src/main/resources/assets/azurelib/textures/item/pistol.png deleted file mode 100644 index 290e21ba10d8170a3d0bb0d087335cfad19524f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1486 zcmV;<1u^=GP)z>$|NsB0RVx4g|G$7>s#Q7Y=;+b8xzTi8xkfIwTQ|da zTC__%@$vD$SwQpi^WMC%yjeHQdQ$7_>%CPu-`Cg2cv#7DQN?so#&}b{Up&KWLBL)* zzhpeZWj)1gLBeZ2$$e78Z9&CvKfj!a%!XRTZ$Qa-Bj$Xy7lF*J>%!5eKk6O%yNzaH&)}3nAn`Y3FPR)l<)S6Y(mQd84 zSJt6f)t^-0xqaHKU)ZQu+_7WcvUJ|GX6D+!<;9HPx^Cynkl?&(*PvkO(3k7fp5ni6 zC=Ay|NsC0{{R2{|Nr{`|M>s@_5S_){{8v> z{rCR<@c#Ym{rvO&`|AArCH#tB@Q&?JFWoKz@adUr#hL4$^qo$~-ud=ndy2HlK(AC-6-s0u#?C$UI@$>Wa z_V)Jo`1twy{Qdp@{{H?9UglZ=00W{)L_t(|+U?bam)kfHfN^b^nVIRB%XrLKW@fG& zYi9oU`{c-Z-aPxTN@;&j8$&_gPRN<6B?^IpO9??t;eBZp*JhHNv=Uu^=900oF*gIV zFgKZjDu5rBN>NBair_)weUnnU116y>U=jua_^W`n38-)Y$9S`sCqO0rn!^Q97OI(; z|u?llB6@!452|x|R8A9HG zw*66-C674+go($1wr2po5fHKk#Gj4{c<-$eLSe4MJNOKTaR9Ti86RRd-ot#%!(P0F zWtf3~62N8tfc7^5>K7qizyttAzJS5IK#f(FJ}&sj3K$?(6t2U1?86VZfMfU`zu-rF zi4UwD)KOJn90jffQhtH9)IX0w4e&0LV0f!PjvLNUs4XVECJW zIdJ#{cX1CFa2^kE61#B*8?YpqgW&*QDIXJ&_8x732N2%}W+h-1$r8Xe6$!#B6JMqP z-w4nV1aQLiQ-C*P6oS8;0I(Xna0@qZ6Ay764{-^{unluD3oi1$EdW&j2diI*g^nh` z$AJ1?Nbke^0qI3JE`Vz>AmdXIf{3;SVCY6?z)bAIA)LSsT*Wn9!FAliY3#-%OhG?@ zjjaw~G4`|{)F7pB14Whq#@q-n9%6v1_lDS`LRPPrE5Q0w_8hClZjFH0V^PKcgh2q* zngR3}^e+Q8;~>7mNj$=RTuQFsJ}zQAT+$Mt%Apa^zu=HJKzzk)5YXQ_zC%V3fD{0= zL4YrVsB*zHBwqj<*IzpZqyW#hD$M}bWeWD=Yn;GC+{a~H!Y$mvQ+$E<(H-zOosh27 z3vkcNM(n{Y+(>TY3ZCF59^eSxhD#;_UXOr9_!K|mChp=WPT(ZI#W~!-co oYtg*tumWz#B-F==fEOO{CwClw<{7>lW&i*H07*qoM6N<$f-?r#K>z>% diff --git a/fabric/src/main/java/mod/azure/azurelib/ClientListener.java b/fabric/src/main/java/mod/azure/azurelib/ClientListener.java index 9e9b91fa9..fe89adfbd 100644 --- a/fabric/src/main/java/mod/azure/azurelib/ClientListener.java +++ b/fabric/src/main/java/mod/azure/azurelib/ClientListener.java @@ -3,20 +3,9 @@ import com.mojang.blaze3d.platform.InputConstants; import mod.azure.azurelib.network.Networking; import mod.azure.azurelib.platform.Services; -import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; -import mod.azure.azurelib.rewrite.render.item.AzItemRendererRegistry; -import mod.azure.azurelib.testing.armor.DoomicornArmorRenderer; -import mod.azure.azurelib.testing.block.be.StargateBlockRenderer; -import mod.azure.azurelib.testing.entity.MarauderRenderer; -import mod.azure.azurelib.testing.item.PistolRenderer; import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; -import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; import net.minecraft.client.KeyMapping; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; import org.lwjgl.glfw.GLFW; public final class ClientListener implements ClientModInitializer { @@ -34,20 +23,5 @@ public void onInitializeClient() { KeyBindingHelper.registerKeyBinding(Keybindings.FIRE_WEAPON); Services.NETWORK.registerClientReceiverPackets(); Networking.PacketRegistry.registerClient(); - - AzItemRendererRegistry.register(FabricAzureLibMod.PISTOL_ITEM, PistolRenderer::new); - AzArmorRendererRegistry.register( - DoomicornArmorRenderer::new, - FabricAzureLibMod.DOOMICORN_HELMET, - FabricAzureLibMod.DOOMICORN_CHESTPLATE, - FabricAzureLibMod.DOOMICORN_LEGGINGS, - FabricAzureLibMod.DOOMICORN_BOOTS - ); - BlockRenderLayerMap.INSTANCE.putBlock(FabricAzureLibMod.STARGATE_BLOCK, RenderType.translucent()); - BlockEntityRenderers.register( - FabricAzureLibMod.STARGATE_BLOCK_ENTITY, - (BlockEntityRendererProvider.Context rendererDispatcherIn) -> new StargateBlockRenderer() - ); - EntityRendererRegistry.register(FabricAzureLibMod.MARAUDER, MarauderRenderer::new); } } diff --git a/fabric/src/main/java/mod/azure/azurelib/FabricAzureLibMod.java b/fabric/src/main/java/mod/azure/azurelib/FabricAzureLibMod.java index d5b398882..6d51d7c0a 100644 --- a/fabric/src/main/java/mod/azure/azurelib/FabricAzureLibMod.java +++ b/fabric/src/main/java/mod/azure/azurelib/FabricAzureLibMod.java @@ -7,40 +7,20 @@ import mod.azure.azurelib.entities.TickingLightBlock; import mod.azure.azurelib.entities.TickingLightEntity; import mod.azure.azurelib.platform.FabricAzureLibNetwork; -import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; -import mod.azure.azurelib.testing.armor.DoomicornArmor; -import mod.azure.azurelib.testing.block.StargateBlock; -import mod.azure.azurelib.testing.block.be.StargateBlockEntity; -import mod.azure.azurelib.testing.entity.MarauderEntity; -import mod.azure.azurelib.testing.item.PistolItem; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; -import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry; import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.entity.*; -import net.minecraft.world.entity.monster.Monster; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.level.block.entity.BlockEntityType; public final class FabricAzureLibMod implements ModInitializer { public static BlockEntityType TICKING_LIGHT_ENTITY; - public static BlockEntityType STARGATE_BLOCK_ENTITY; public static final TickingLightBlock TICKING_LIGHT_BLOCK = new TickingLightBlock(); - public static final StargateBlock STARGATE_BLOCK = new StargateBlock(); public static final Enchantment INCENDIARYENCHANTMENT = new IncendiaryEnchantment(Enchantment.Rarity.RARE, EquipmentSlot.MAINHAND); - public static final EntityType MARAUDER = mob("marauder", MarauderEntity::new, 1.5F, 2.5F); - - public static final Item PISTOL_ITEM = new PistolItem(); - public static final Item DOOMICORN_HELMET = new DoomicornArmor(ArmorItem.Type.HELMET); - public static final Item DOOMICORN_CHESTPLATE = new DoomicornArmor(ArmorItem.Type.CHESTPLATE); - public static final Item DOOMICORN_LEGGINGS = new DoomicornArmor(ArmorItem.Type.LEGGINGS); - public static final Item DOOMICORN_BOOTS = new DoomicornArmor(ArmorItem.Type.BOOTS); @Override public void onInitialize() { @@ -51,30 +31,10 @@ public void onInitialize() { Registry.register(BuiltInRegistries.BLOCK, AzureLib.modResource("lightblock"), FabricAzureLibMod.TICKING_LIGHT_BLOCK); FabricAzureLibMod.TICKING_LIGHT_ENTITY = Registry.register(BuiltInRegistries.BLOCK_ENTITY_TYPE, AzureLib.MOD_ID + ":lightblock", FabricBlockEntityTypeBuilder.create(TickingLightEntity::new, FabricAzureLibMod.TICKING_LIGHT_BLOCK).build(null)); - Registry.register(BuiltInRegistries.BLOCK, AzureLib.modResource("stargate"), FabricAzureLibMod.STARGATE_BLOCK); - FabricAzureLibMod.STARGATE_BLOCK_ENTITY = Registry.register(BuiltInRegistries.BLOCK_ENTITY_TYPE, AzureLib.MOD_ID + ":stargate", FabricBlockEntityTypeBuilder.create(StargateBlockEntity::new, FabricAzureLibMod.STARGATE_BLOCK).build(null)); - FabricDefaultAttributeRegistry.register( - MARAUDER, - Monster.createMonsterAttributes() - ); ServerLifecycleEvents.SERVER_STOPPING.register((server) -> ConfigIO.FILE_WATCH_MANAGER.stopService()); if (AzureLibMod.config.useIncendiaryEnchantment) Registry.register(BuiltInRegistries.ENCHANTMENT, AzureLib.modResource("incendiaryenchantment"), INCENDIARYENCHANTMENT); - - Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("pistol"), PISTOL_ITEM); - Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("doomicorn_helmet"), DOOMICORN_HELMET); - Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("doomicorn_chestplate"), DOOMICORN_CHESTPLATE); - Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("doomicorn_leggings"), DOOMICORN_LEGGINGS); - Registry.register(BuiltInRegistries.ITEM, AzureLib.modResource("doomicorn_boots"), DOOMICORN_BOOTS); - - AzIdentityRegistry.register( - PISTOL_ITEM, - DOOMICORN_HELMET, - DOOMICORN_CHESTPLATE, - DOOMICORN_LEGGINGS, - DOOMICORN_BOOTS - ); } private static EntityType mob(String id, EntityType.EntityFactory factory, float height, float width) { diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java deleted file mode 100644 index 8e8d75432..000000000 --- a/fabric/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java +++ /dev/null @@ -1,53 +0,0 @@ -package mod.azure.azurelib.testing.block; - -import mod.azure.azurelib.FabricAzureLibMod; -import mod.azure.azurelib.testing.block.be.StargateBlockEntity; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.BaseEntityBlock; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityTicker; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class StargateBlock extends BaseEntityBlock { - - public StargateBlock() { - super(BlockBehaviour.Properties.of().sound(SoundType.DRIPSTONE_BLOCK).strength(5.0f, 8.0f).noOcclusion()); - } - - /** - * Creates a new {@link BlockEntity} instance for the Stargate block at the specified position and state. - * - * @param pos The position of the block in the world. - * @param state The current block state for this block entity. - * @return A new {@link BlockEntity} instance associated with the Stargate block, or {@code null} if none is - * available. - */ - @Override - public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos pos, @NotNull BlockState state) { - return FabricAzureLibMod.STARGATE_BLOCK_ENTITY.create(pos, state); - } - - /** - * Determines the appropriate ticker for a block entity to handle its periodic updates. - * - * @param level The current level or world instance. - * @param state The block state of the associated block. - * @param type The type of the block entity to obtain the ticker for. - * @param A subtype of BlockEntity. - * @return A BlockEntityTicker for the specified block entity type, or null if no ticker is applicable. - */ - @Override - public BlockEntityTicker getTicker( - @NotNull Level level, - @NotNull BlockState state, - @NotNull BlockEntityType type - ) { - return createTickerHelper(type, FabricAzureLibMod.STARGATE_BLOCK_ENTITY, StargateBlockEntity::tick); - } -} diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java deleted file mode 100644 index 2ef220aac..000000000 --- a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java +++ /dev/null @@ -1,26 +0,0 @@ -package mod.azure.azurelib.testing.block.be; - -import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; -import mod.azure.azurelib.testing.CommonStrings; - -/** - * The StargateBlockAnimationDispatcher class is responsible for managing and triggering animation commands for block - * entities, specifically for the Stargate block entity. - */ -public class StargateBlockAnimationDispatcher { - - private static final AzCommand SPINNING_COMMAND = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.SPIN_ANIMATION_NAME - ); - - private final StargateBlockEntity stargateBlockEntity; - - public StargateBlockAnimationDispatcher(StargateBlockEntity stargateBlockEntity) { - this.stargateBlockEntity = stargateBlockEntity; - } - - public void serverSpin() { - SPINNING_COMMAND.sendForBlockEntity(stargateBlockEntity); - } -} diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java deleted file mode 100644 index bf053d520..000000000 --- a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java +++ /dev/null @@ -1,32 +0,0 @@ -package mod.azure.azurelib.testing.block.be; - -import mod.azure.azurelib.FabricAzureLibMod; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; - -public class StargateBlockEntity extends BlockEntity { - - public final StargateBlockAnimationDispatcher animationDispatcher; - - public StargateBlockEntity(BlockPos pos, BlockState blockState) { - super(FabricAzureLibMod.STARGATE_BLOCK_ENTITY, pos, blockState); - this.animationDispatcher = new StargateBlockAnimationDispatcher(this); - } - - /** - * Handles the tick behavior for the StargateBlockEntity, triggering server-side spinning animations if the block - * entity and level instance are valid and the method is executed on the client side. - * - * @param level The current level or world instance. - * @param pos The position of the block in the world. - * @param state The current block state of the associated block. - * @param blockEntity The StargateBlockEntity instance to operate on. - */ - public static void tick(Level level, BlockPos pos, BlockState state, StargateBlockEntity blockEntity) { - if (blockEntity.level != null && level.isClientSide()) { - blockEntity.animationDispatcher.serverSpin(); - } - } -} diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java deleted file mode 100644 index dd9e259cd..000000000 --- a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java +++ /dev/null @@ -1,38 +0,0 @@ -package mod.azure.azurelib.testing.block.be; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; -import mod.azure.azurelib.rewrite.animation.impl.AzBlockAnimator; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; - -/** - * StargateBlockEntityAnimator is responsible for managing and configuring animations for the StargateBlockEntity. It - * defines specific animations and registers them with the animation controller system, enabling dynamic and interactive - * visual effects based on the block entity's state. - */ -public class StargateBlockEntityAnimator extends AzBlockAnimator { - - private static final ResourceLocation ANIMATIONS = AzureLib.modResource( - "animations/block/stargate.animation.json" - ); - - protected StargateBlockEntityAnimator() { - super(AzAnimatorConfig.defaultConfig()); - } - - @Override - public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { - animationControllerContainer.add( - AzAnimationController.builder(this, "base_controller") - .build() - ); - } - - @Override - public @NotNull ResourceLocation getAnimationLocation(StargateBlockEntity animatable) { - return ANIMATIONS; - } -} diff --git a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java b/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java deleted file mode 100644 index 49be22d61..000000000 --- a/fabric/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java +++ /dev/null @@ -1,21 +0,0 @@ -package mod.azure.azurelib.testing.block.be; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRenderer; -import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRendererConfig; -import net.minecraft.resources.ResourceLocation; - -public class StargateBlockRenderer extends AzBlockEntityRenderer { - - private static final ResourceLocation MODEL = AzureLib.modResource("geo/block/stargate.geo.json"); - - private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/block/stargate.png"); - - public StargateBlockRenderer() { - super( - AzBlockEntityRendererConfig.builder(MODEL, TEXTURE) - .setAnimatorProvider(StargateBlockEntityAnimator::new) - .build() - ); - } -} diff --git a/neo/src/main/java/mod/azure/azurelib/ClientModListener.java b/neo/src/main/java/mod/azure/azurelib/ClientModListener.java index 813ef241f..e92e9073d 100644 --- a/neo/src/main/java/mod/azure/azurelib/ClientModListener.java +++ b/neo/src/main/java/mod/azure/azurelib/ClientModListener.java @@ -2,10 +2,8 @@ import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyMapping; -import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.ConfigScreenHandler; -import net.minecraftforge.client.event.EntityRenderersEvent; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModContainer; @@ -20,37 +18,10 @@ import mod.azure.azurelib.client.AzureLibClient; import mod.azure.azurelib.config.ConfigHolder; -import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry; -import mod.azure.azurelib.rewrite.render.item.AzItemRendererRegistry; -import mod.azure.azurelib.testing.armor.DoomicornArmorRenderer; -import mod.azure.azurelib.testing.block.be.StargateBlockRenderer; -import mod.azure.azurelib.testing.entity.MarauderRenderer; -import mod.azure.azurelib.testing.item.PistolRenderer; @EventBusSubscriber(modid = AzureLib.MOD_ID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) public class ClientModListener { - @SubscribeEvent - public static void onClientSetup(final FMLClientSetupEvent event) { - AzItemRendererRegistry.register(NeoForgeAzureLibMod.AzureItems.PISTOL_ITEM.get(), PistolRenderer::new); - AzArmorRendererRegistry.register( - DoomicornArmorRenderer::new, - NeoForgeAzureLibMod.AzureItems.DOOMICORN_HELMET.get(), - NeoForgeAzureLibMod.AzureItems.DOOMICORN_CHESTPLATE.get(), - NeoForgeAzureLibMod.AzureItems.DOOMICORN_LEGGINGS.get(), - NeoForgeAzureLibMod.AzureItems.DOOMICORN_BOOTS.get() - ); - } - - @SubscribeEvent - public static void registerRenderers(final EntityRenderersEvent.RegisterRenderers event) { - event.registerEntityRenderer(NeoForgeAzureLibMod.AzureEntities.MARAUDER.get(), MarauderRenderer::new); - event.registerBlockEntityRenderer( - NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), - (BlockEntityRendererProvider.Context rendererDispatcherIn) -> new StargateBlockRenderer() - ); - } - @SubscribeEvent public static void registerKeys(final RegisterKeyMappingsEvent event) { Keybindings.RELOAD = new KeyMapping( diff --git a/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java b/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java index 90e4c926e..5f1ee7807 100644 --- a/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java +++ b/neo/src/main/java/mod/azure/azurelib/NeoForgeAzureLibMod.java @@ -2,14 +2,10 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.entity.monster.Monster; -import net.minecraft.world.item.ArmorItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraftforge.event.entity.EntityAttributeCreationEvent; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; @@ -25,12 +21,6 @@ import mod.azure.azurelib.entities.TickingLightBlock; import mod.azure.azurelib.entities.TickingLightEntity; import mod.azure.azurelib.network.Networking; -import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry; -import mod.azure.azurelib.testing.armor.DoomicornArmor; -import mod.azure.azurelib.testing.block.StargateBlock; -import mod.azure.azurelib.testing.block.be.StargateBlockEntity; -import mod.azure.azurelib.testing.entity.MarauderEntity; -import mod.azure.azurelib.testing.item.PistolItem; @Mod.EventBusSubscriber @Mod(AzureLib.MOD_ID) @@ -50,8 +40,6 @@ public NeoForgeAzureLibMod() { AzureEntities.TILE_TYPES.register(modEventBus); AzureEntities.ENTITY_TYPES.register(modEventBus); AzureItems.ITEMS.register(modEventBus); - modEventBus.addListener(this::createEntityAttributes); - modEventBus.addListener(this::commonSetup); } private void init(FMLCommonSetupEvent event) { @@ -83,8 +71,6 @@ public class AzureBlocks { "lightblock", TickingLightBlock::new ); - - public static final RegistryObject STARGATE_BLOCK = BLOCKS.register("stargate", StargateBlock::new); } public class AzureEntities { @@ -105,20 +91,6 @@ public class AzureEntities { () -> BlockEntityType.Builder.of(TickingLightEntity::new, AzureBlocks.TICKING_LIGHT_BLOCK.get()) .build(null) ); - - public static final RegistryObject> STARGATE_BLOCK_ENTITY = TILE_TYPES - .register( - "stargate", - () -> BlockEntityType.Builder.of(StargateBlockEntity::new, AzureBlocks.STARGATE_BLOCK.get()).build(null) - ); - - public static final RegistryObject> MARAUDER = ENTITY_TYPES.register( - "maruader", - () -> EntityType.Builder.of(MarauderEntity::new, MobCategory.MONSTER) - .sized(0.6F, 1.95F) - .clientTrackingRange(8) - .build(AzureLib.MOD_ID + ":marauder") - ); } public class AzureItems { @@ -127,49 +99,5 @@ public class AzureItems { ForgeRegistries.ITEMS, AzureLib.MOD_ID ); - - public static final RegistryObject PISTOL_ITEM = ITEMS.register("pistol", () -> new PistolItem()); - - public static final RegistryObject DOOMICORN_HELMET = ITEMS.register( - "doomicorn_helmet", - () -> new DoomicornArmor( - ArmorItem.Type.HELMET - ) - ); - - public static final RegistryObject DOOMICORN_CHESTPLATE = ITEMS.register( - "doomicorn_chestplate", - () -> new DoomicornArmor( - ArmorItem.Type.CHESTPLATE - ) - ); - - public static final RegistryObject DOOMICORN_LEGGINGS = ITEMS.register( - "doomicorn_leggings", - () -> new DoomicornArmor( - ArmorItem.Type.LEGGINGS - ) - ); - - public static final RegistryObject DOOMICORN_BOOTS = ITEMS.register( - "doomicorn_boots", - () -> new DoomicornArmor( - ArmorItem.Type.BOOTS - ) - ); - } - - public void createEntityAttributes(final EntityAttributeCreationEvent event) { - event.put(AzureEntities.MARAUDER.get(), Monster.createMonsterAttributes().build()); - } - - public void commonSetup(final FMLCommonSetupEvent event) { - AzIdentityRegistry.register( - AzureItems.PISTOL_ITEM.get(), - AzureItems.DOOMICORN_HELMET.get(), - AzureItems.DOOMICORN_CHESTPLATE.get(), - AzureItems.DOOMICORN_LEGGINGS.get(), - AzureItems.DOOMICORN_BOOTS.get() - ); } } diff --git a/neo/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java b/neo/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java deleted file mode 100644 index 621768110..000000000 --- a/neo/src/main/java/mod/azure/azurelib/testing/block/StargateBlock.java +++ /dev/null @@ -1,57 +0,0 @@ -package mod.azure.azurelib.testing.block; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.BaseEntityBlock; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityTicker; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import mod.azure.azurelib.NeoForgeAzureLibMod; -import mod.azure.azurelib.testing.block.be.StargateBlockEntity; - -public class StargateBlock extends BaseEntityBlock { - - public StargateBlock() { - super(Properties.of().sound(SoundType.DRIPSTONE_BLOCK).strength(5.0f, 8.0f).noOcclusion()); - } - - /** - * Creates a new {@link BlockEntity} instance for the Stargate block at the specified position and state. - * - * @param pos The position of the block in the world. - * @param state The current block state for this block entity. - * @return A new {@link BlockEntity} instance associated with the Stargate block, or {@code null} if none is - * available. - */ - @Override - public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos pos, @NotNull BlockState state) { - return NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get().create(pos, state); - } - - /** - * Determines the appropriate ticker for a block entity to handle its periodic updates. - * - * @param level The current level or world instance. - * @param state The block state of the associated block. - * @param type The type of the block entity to obtain the ticker for. - * @param A subtype of BlockEntity. - * @return A BlockEntityTicker for the specified block entity type, or null if no ticker is applicable. - */ - @Override - public BlockEntityTicker getTicker( - @NotNull Level level, - @NotNull BlockState state, - @NotNull BlockEntityType type - ) { - return createTickerHelper( - type, - NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), - StargateBlockEntity::tick - ); - } -} diff --git a/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java deleted file mode 100644 index 2ef220aac..000000000 --- a/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockAnimationDispatcher.java +++ /dev/null @@ -1,26 +0,0 @@ -package mod.azure.azurelib.testing.block.be; - -import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand; -import mod.azure.azurelib.testing.CommonStrings; - -/** - * The StargateBlockAnimationDispatcher class is responsible for managing and triggering animation commands for block - * entities, specifically for the Stargate block entity. - */ -public class StargateBlockAnimationDispatcher { - - private static final AzCommand SPINNING_COMMAND = AzCommand.create( - CommonStrings.BASE_CONTROLLER, - CommonStrings.SPIN_ANIMATION_NAME - ); - - private final StargateBlockEntity stargateBlockEntity; - - public StargateBlockAnimationDispatcher(StargateBlockEntity stargateBlockEntity) { - this.stargateBlockEntity = stargateBlockEntity; - } - - public void serverSpin() { - SPINNING_COMMAND.sendForBlockEntity(stargateBlockEntity); - } -} diff --git a/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java deleted file mode 100644 index 505f0172f..000000000 --- a/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntity.java +++ /dev/null @@ -1,33 +0,0 @@ -package mod.azure.azurelib.testing.block.be; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; - -import mod.azure.azurelib.NeoForgeAzureLibMod; - -public class StargateBlockEntity extends BlockEntity { - - public final StargateBlockAnimationDispatcher animationDispatcher; - - public StargateBlockEntity(BlockPos pos, BlockState blockState) { - super(NeoForgeAzureLibMod.AzureEntities.STARGATE_BLOCK_ENTITY.get(), pos, blockState); - this.animationDispatcher = new StargateBlockAnimationDispatcher(this); - } - - /** - * Handles the tick behavior for the StargateBlockEntity, triggering server-side spinning animations if the block - * entity and level instance are valid and the method is executed on the client side. - * - * @param level The current level or world instance. - * @param pos The position of the block in the world. - * @param state The current block state of the associated block. - * @param blockEntity The StargateBlockEntity instance to operate on. - */ - public static void tick(Level level, BlockPos pos, BlockState state, StargateBlockEntity blockEntity) { - if (blockEntity.level != null && level.isClientSide()) { - blockEntity.animationDispatcher.serverSpin(); - } - } -} diff --git a/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java deleted file mode 100644 index a7b639726..000000000 --- a/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockEntityAnimator.java +++ /dev/null @@ -1,39 +0,0 @@ -package mod.azure.azurelib.testing.block.be; - -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.animation.AzAnimatorConfig; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController; -import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer; -import mod.azure.azurelib.rewrite.animation.impl.AzBlockAnimator; - -/** - * StargateBlockEntityAnimator is responsible for managing and configuring animations for the StargateBlockEntity. It - * defines specific animations and registers them with the animation controller system, enabling dynamic and interactive - * visual effects based on the block entity's state. - */ -public class StargateBlockEntityAnimator extends AzBlockAnimator { - - private static final ResourceLocation ANIMATIONS = AzureLib.modResource( - "animations/block/stargate.animation.json" - ); - - protected StargateBlockEntityAnimator() { - super(AzAnimatorConfig.defaultConfig()); - } - - @Override - public void registerControllers(AzAnimationControllerContainer animationControllerContainer) { - animationControllerContainer.add( - AzAnimationController.builder(this, "base_controller") - .build() - ); - } - - @Override - public @NotNull ResourceLocation getAnimationLocation(StargateBlockEntity animatable) { - return ANIMATIONS; - } -} diff --git a/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java b/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java deleted file mode 100644 index efe46da18..000000000 --- a/neo/src/main/java/mod/azure/azurelib/testing/block/be/StargateBlockRenderer.java +++ /dev/null @@ -1,22 +0,0 @@ -package mod.azure.azurelib.testing.block.be; - -import net.minecraft.resources.ResourceLocation; - -import mod.azure.azurelib.AzureLib; -import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRenderer; -import mod.azure.azurelib.rewrite.render.block.AzBlockEntityRendererConfig; - -public class StargateBlockRenderer extends AzBlockEntityRenderer { - - private static final ResourceLocation MODEL = AzureLib.modResource("geo/block/stargate.geo.json"); - - private static final ResourceLocation TEXTURE = AzureLib.modResource("textures/block/stargate.png"); - - public StargateBlockRenderer() { - super( - AzBlockEntityRendererConfig.builder(MODEL, TEXTURE) - .setAnimatorProvider(StargateBlockEntityAnimator::new) - .build() - ); - } -} From 326a9f6c51e0deb60639c5cae6bd64b134ed728c Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Thu, 15 May 2025 22:44:50 -0400 Subject: [PATCH 39/40] v3.0.0 --- changelog.md | 25 +++---------------------- gradle.properties | 2 +- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/changelog.md b/changelog.md index 2c9b4435b..c4f548f8b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,22 +1,3 @@ -TODO for 3.x port -- mod.azure.azurelib.network.packet.AzBlockEntityDispatchCommandPacket -- mod.azure.azurelib.network.packet.AzEntityDispatchCommandPacket -- mod.azure.azurelib.network.packet.AzItemStackDispatchCommandPacket -- mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide -- mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand -- mod.azure.azurelib.rewrite.animation.dispatch.command.action.AzAction -- mod.azure.azurelib.rewrite.animation.dispatch.command.action.codec.AzActionCodec -- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootCancelAction -- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootPlayAnimationSequenceAction -- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootSetAnimationSpeedAction -- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootSetEasingTypeAction -- mod.azure.azurelib.rewrite.animation.dispatch.command.action.impl.root.AzRootSetTransitionSpeedAction -- mod.azure.azurelib.rewrite.animation.dispatch.command.action.registry.AzActionRegistry -- mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequence -- mod.azure.azurelib.rewrite.animation.dispatch.command.stage.AzAnimationStage -- mod.azure.azurelib.rewrite.animation.easing.AzEasingType -- mod.azure.azurelib.rewrite.animation.property.AzAnimationProperties -- mod.azure.azurelib.rewrite.animation.property.AzAnimationStageProperties -- mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationPropertiesCodec -- mod.azure.azurelib.rewrite.animation.property.codec.AzAnimationStagePropertiesCodec -- mod.azure.azurelib.rewrite.util.codec.AzListStreamCodec \ No newline at end of file +v3.0.0 + +- Complete backport of AzureLib 3.x, please see: https://moddedmc.wiki/en/project/azurelib/docs Updating Guide for updating to this version. \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 18a2d4a1a..5b60ef749 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,7 +34,7 @@ modmenu_version = 7.2.2 maven_group = mod.azure.azurelib mod_name = AzureLib mod_author = AzureDoom -version = 2.0.41 +version = 3.0.0 mod_id = azurelib maven_url = https://maven.azuredoom.com/mods mod_license = MIT From 924db487a5a4a63696282787e5d345d1df2c2b6d Mon Sep 17 00:00:00 2001 From: AzureZhen <7415711+AzureDoom@users.noreply.github.com> Date: Thu, 15 May 2025 22:59:04 -0400 Subject: [PATCH 40/40] Update AzureNavigation.java --- .../java/mod/azure/azurelib/ai/pathing/AzureNavigation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java index 3086c8101..ff06c9692 100644 --- a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java +++ b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java @@ -114,7 +114,7 @@ public boolean moveTo(Entity entity, double d) { /** * Ensures a minimum width of 1.0 for entities, addressing an issue - * where smaller entities (< 0.8 units in width) encounter pathfinding + * where smaller entities (less than 0.8 units in width) encounter pathfinding * failures. This resolves bugs such as MC-226637, where small entities * end up "spinning" due to improper navigation logic. *