feat: add cosmetics system
This commit is contained in:
parent
274e2e1876
commit
623f394486
10 changed files with 612 additions and 0 deletions
|
@ -8,6 +8,7 @@ import net.minecraftforge.common.MinecraftForge;
|
|||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import software.bernie.geckolib3.GeckoLib;
|
||||
|
||||
@Mod("ntx4core")
|
||||
public class Ntx4Core {
|
||||
|
@ -20,6 +21,8 @@ public class Ntx4Core {
|
|||
Ntx4CoreBlocks.BLOCKS.register(bus);
|
||||
Ntx4CoreItems.ITEMS.register(bus);
|
||||
|
||||
GeckoLib.initialize();
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(Ntx4CoreShaders.class);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package net.anvilcraft.ntx4core.cosmetics;
|
||||
|
||||
import net.anvilcraft.ntx4core.Ntx4Core;
|
||||
import net.minecraft.client.render.entity.PlayerEntityRenderer;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.EntityRenderersEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
|
||||
|
||||
@EventBusSubscriber(modid = Ntx4Core.MODID, bus = Bus.MOD, value = {Dist.CLIENT})
|
||||
public class ClientEventHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void clientSetup(EntityRenderersEvent.AddLayers event) {
|
||||
Ntx4Core.LOGGER.info("Client Setup: adding CosmeticLayer");
|
||||
for (String skin : event.getSkins()){
|
||||
Ntx4Core.LOGGER.info("Client Setup: player check");
|
||||
if (event.getSkin(skin) instanceof PlayerEntityRenderer render) {
|
||||
Ntx4Core.LOGGER.info("Client Setup: added CosmeticLayer");
|
||||
render.addFeature(new CosmeticFeatureRenderer(render, skin));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
package net.anvilcraft.ntx4core.cosmetics;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.model.BipedEntityModel;
|
||||
import net.minecraft.client.render.entity.model.EntityModelLayers;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Matrix4f;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.Vec3f;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import org.jetbrains.annotations.ApiStatus.AvailableSince;
|
||||
import software.bernie.geckolib3.compat.PatchouliCompat;
|
||||
import software.bernie.geckolib3.core.IAnimatable;
|
||||
import software.bernie.geckolib3.core.IAnimatableModel;
|
||||
import software.bernie.geckolib3.core.controller.AnimationController;
|
||||
import software.bernie.geckolib3.core.controller.AnimationController.ModelFetcher;
|
||||
import software.bernie.geckolib3.core.event.predicate.AnimationEvent;
|
||||
import software.bernie.geckolib3.core.processor.IBone;
|
||||
import software.bernie.geckolib3.core.util.Color;
|
||||
import software.bernie.geckolib3.geo.render.built.GeoBone;
|
||||
import software.bernie.geckolib3.geo.render.built.GeoModel;
|
||||
import software.bernie.geckolib3.model.AnimatedGeoModel;
|
||||
import software.bernie.geckolib3.renderers.geo.IGeoRenderer;
|
||||
import software.bernie.geckolib3.util.EModelRenderCycle;
|
||||
import software.bernie.geckolib3.util.GeoUtils;
|
||||
import software.bernie.geckolib3.util.IRenderCycle;
|
||||
import software.bernie.geckolib3.util.RenderUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
public class CosmeticArmorRenderer extends BipedEntityModel
|
||||
implements IGeoRenderer<CosmeticItem>, ModelFetcher<CosmeticItem> {
|
||||
|
||||
protected CosmeticItem currentArmorItem;
|
||||
protected LivingEntity entityLiving;
|
||||
protected float widthScale = 1;
|
||||
protected float heightScale = 1;
|
||||
protected Matrix4f dispatchedMat = new Matrix4f();
|
||||
protected Matrix4f renderEarlyMat = new Matrix4f();
|
||||
|
||||
public String headBone = null;
|
||||
public String bodyBone = null;
|
||||
public String rightArmBone = null;
|
||||
public String leftArmBone = null;
|
||||
public String rightLegBone = null;
|
||||
public String leftLegBone = null;
|
||||
|
||||
private final AnimatedGeoModel<CosmeticItem> modelProvider;
|
||||
|
||||
protected VertexConsumerProvider rtb = null;
|
||||
|
||||
private IRenderCycle currentModelRenderCycle = EModelRenderCycle.INITIAL;
|
||||
|
||||
{
|
||||
AnimationController.addModelFetcher(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public IAnimatableModel<CosmeticItem> apply(IAnimatable t) {
|
||||
if (t instanceof CosmeticItem) return this.getGeoModelProvider();
|
||||
return null;
|
||||
}
|
||||
|
||||
public CosmeticArmorRenderer() {
|
||||
super(MinecraftClient.getInstance().getEntityModelLoader().getModelPart(EntityModelLayers.PLAYER_INNER_ARMOR));
|
||||
|
||||
this.modelProvider = new CosmeticModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay,
|
||||
float red, float green, float blue, float alpha) {
|
||||
this.render(0, poseStack, buffer, packedLight);
|
||||
}
|
||||
|
||||
public void render(float partialTick, MatrixStack poseStack, VertexConsumer buffer, int packedLight) {
|
||||
GeoModel model = this.modelProvider.getModel(this.modelProvider.getModelLocation(this.currentArmorItem));
|
||||
AnimationEvent animationEvent = new AnimationEvent(this.currentArmorItem, 0, 0,
|
||||
MinecraftClient.getInstance().getTickDelta(), false, // can also be getLastFrameDuration()
|
||||
Arrays.asList(this.entityLiving));
|
||||
|
||||
poseStack.push();
|
||||
poseStack.translate(0, 24 / 16F, 0);
|
||||
poseStack.scale(-1, -1, 1);
|
||||
|
||||
//this.dispatchedMat = poseStack.last().pose().copy();
|
||||
this.dispatchedMat = poseStack.peek().getPositionMatrix().copy();
|
||||
|
||||
this.modelProvider.setLivingAnimations(this.currentArmorItem, getInstanceId(this.currentArmorItem), animationEvent);
|
||||
setCurrentModelRenderCycle(EModelRenderCycle.INITIAL);
|
||||
fitToBiped();
|
||||
RenderSystem.setShaderTexture(0, getTextureLocation(this.currentArmorItem));
|
||||
|
||||
Color renderColor = getRenderColor(this.currentArmorItem, partialTick, poseStack, null, buffer, packedLight);
|
||||
RenderLayer renderType = getRenderType(this.currentArmorItem, partialTick, poseStack, null, buffer, packedLight,
|
||||
getTextureLocation(this.currentArmorItem));
|
||||
|
||||
render(model, this.currentArmorItem, partialTick, renderType, poseStack, null, buffer, packedLight,
|
||||
OverlayTexture.DEFAULT_UV, renderColor.getRed() / 255f, renderColor.getGreen() / 255f,
|
||||
renderColor.getBlue() / 255f, renderColor.getAlpha() / 255f);
|
||||
|
||||
if (ModList.get().isLoaded("patchouli"))
|
||||
PatchouliCompat.patchouliLoaded(poseStack);
|
||||
|
||||
poseStack.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderEarly(CosmeticItem animatable, MatrixStack poseStack, float partialTick, VertexConsumerProvider bufferSource,
|
||||
VertexConsumer buffer, int packedLight, int packedOverlay, float red, float green, float blue,
|
||||
float alpha) {
|
||||
//this.renderEarlyMat = poseStack.last().pose().copy();
|
||||
this.renderEarlyMat = poseStack.peek().getPositionMatrix().copy();
|
||||
this.currentArmorItem = animatable;
|
||||
|
||||
IGeoRenderer.super.renderEarly(animatable, poseStack, partialTick, bufferSource, buffer,
|
||||
packedLight, packedOverlay, red, green, blue, alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderRecursively(GeoBone bone, MatrixStack poseStack, VertexConsumer buffer, int packedLight,
|
||||
int packedOverlay, float red, float green, float blue, float alpha) {
|
||||
if (bone.isTrackingXform()) {
|
||||
//Matrix4f poseState = poseStack.last().pose();
|
||||
Matrix4f poseState = poseStack.peek().getPositionMatrix();
|
||||
Vec3d renderOffset = getRenderOffset(this.currentArmorItem, 1);
|
||||
Matrix4f localMatrix = RenderUtils.invertAndMultiplyMatrices(poseState, this.dispatchedMat);
|
||||
|
||||
bone.setModelSpaceXform(RenderUtils.invertAndMultiplyMatrices(poseState, this.renderEarlyMat));
|
||||
//localMatrix.translate(new Vec3f(renderOffset));
|
||||
localMatrix.addToLastColumn(new Vec3f(renderOffset));
|
||||
bone.setLocalSpaceXform(localMatrix);
|
||||
}
|
||||
|
||||
IGeoRenderer.super.renderRecursively(bone, poseStack, buffer, packedLight, packedOverlay, red, green, blue,
|
||||
alpha);
|
||||
}
|
||||
|
||||
public Vec3d getRenderOffset(CosmeticItem entity, float partialTick) {
|
||||
return Vec3d.ZERO;
|
||||
}
|
||||
|
||||
protected void fitToBiped() {
|
||||
if (this.headBone != null) {
|
||||
IBone headBone = this.modelProvider.getBone(this.headBone);
|
||||
|
||||
GeoUtils.copyRotations(this.head, headBone);
|
||||
headBone.setPositionX(this.head.pivotX);
|
||||
headBone.setPositionY(-this.head.pivotY);
|
||||
headBone.setPositionZ(this.head.pivotZ);
|
||||
}
|
||||
|
||||
if (this.bodyBone != null) {
|
||||
IBone bodyBone = this.modelProvider.getBone(this.bodyBone);
|
||||
|
||||
GeoUtils.copyRotations(this.body, bodyBone);
|
||||
bodyBone.setPositionX(this.body.pivotX);
|
||||
bodyBone.setPositionY(-this.body.pivotY);
|
||||
bodyBone.setPositionZ(this.body.pivotZ);
|
||||
}
|
||||
|
||||
if (this.rightArmBone != null) {
|
||||
IBone rightArmBone = this.modelProvider.getBone(this.rightArmBone);
|
||||
|
||||
GeoUtils.copyRotations(this.rightArm, rightArmBone);
|
||||
rightArmBone.setPositionX(this.rightArm.pivotX + 5);
|
||||
rightArmBone.setPositionY(2 - this.rightArm.pivotY);
|
||||
rightArmBone.setPositionZ(this.rightArm.pivotZ);
|
||||
}
|
||||
|
||||
if (this.leftArmBone != null) {
|
||||
IBone leftArmBone = this.modelProvider.getBone(this.leftArmBone);
|
||||
|
||||
GeoUtils.copyRotations(this.leftArm, leftArmBone);
|
||||
leftArmBone.setPositionX(this.leftArm.pivotX - 5);
|
||||
leftArmBone.setPositionY(2 - this.leftArm.pivotY);
|
||||
leftArmBone.setPositionZ(this.leftArm.pivotZ);
|
||||
}
|
||||
|
||||
if (this.rightLegBone != null) {
|
||||
IBone rightLegBone = this.modelProvider.getBone(this.rightLegBone);
|
||||
|
||||
GeoUtils.copyRotations(this.rightLeg, rightLegBone);
|
||||
rightLegBone.setPositionX(this.rightLeg.pivotX + 2);
|
||||
rightLegBone.setPositionY(12 - this.rightLeg.pivotY);
|
||||
rightLegBone.setPositionZ(this.rightLeg.pivotZ);
|
||||
}
|
||||
|
||||
if (this.leftLegBone != null) {
|
||||
IBone leftLegBone = this.modelProvider.getBone(this.leftLegBone);
|
||||
|
||||
GeoUtils.copyRotations(this.leftLeg, leftLegBone);
|
||||
leftLegBone.setPositionX(this.leftLeg.pivotX - 2);
|
||||
leftLegBone.setPositionY(12 - this.leftLeg.pivotY);
|
||||
leftLegBone.setPositionZ(this.leftLeg.pivotZ);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatedGeoModel<CosmeticItem> getGeoModelProvider() {
|
||||
return this.modelProvider;
|
||||
}
|
||||
|
||||
@AvailableSince(value = "3.1.24")
|
||||
@Override
|
||||
@Nonnull
|
||||
public IRenderCycle getCurrentModelRenderCycle() {
|
||||
return this.currentModelRenderCycle;
|
||||
}
|
||||
|
||||
@AvailableSince(value = "3.1.24")
|
||||
@Override
|
||||
public void setCurrentModelRenderCycle(IRenderCycle currentModelRenderCycle) {
|
||||
this.currentModelRenderCycle = currentModelRenderCycle;
|
||||
}
|
||||
|
||||
@AvailableSince(value = "3.1.24")
|
||||
@Override
|
||||
public float getWidthScale(CosmeticItem animatable) {
|
||||
return this.widthScale;
|
||||
}
|
||||
|
||||
@AvailableSince(value = "3.1.24")
|
||||
@Override
|
||||
public float getHeightScale(CosmeticItem entity) {
|
||||
return this.heightScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getTextureLocation(CosmeticItem animatable) {
|
||||
return this.modelProvider.getTextureLocation(animatable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Everything after this point needs to be called every frame before rendering
|
||||
*/
|
||||
public CosmeticArmorRenderer setCurrentItem(LivingEntity entity, CosmeticItem item) {
|
||||
this.entityLiving = entity;
|
||||
this.currentArmorItem = item;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public final CosmeticArmorRenderer applyEntityStats(BipedEntityModel defaultArmor) {
|
||||
this.child = defaultArmor.child;
|
||||
this.sneaking = defaultArmor.sneaking;
|
||||
this.riding = defaultArmor.riding;
|
||||
this.rightArmPose = defaultArmor.rightArmPose;
|
||||
this.leftArmPose = defaultArmor.leftArmPose;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void filterBones(){
|
||||
this.headBone = getCurrentCosmetic().getHead();
|
||||
this.bodyBone = getCurrentCosmetic().getBody();
|
||||
this.leftArmBone = getCurrentCosmetic().getLeftArm();
|
||||
this.rightArmBone = getCurrentCosmetic().getRightArm();
|
||||
this.leftLegBone = getCurrentCosmetic().getLeftLeg();
|
||||
this.rightLegBone = getCurrentCosmetic().getRightLeg();
|
||||
|
||||
getGeoModelProvider().getModel(getCurrentCosmetic().getModelLocation());
|
||||
|
||||
this.setBoneVisibility(this.headBone, getCurrentCosmetic().getHead() != null);
|
||||
this.setBoneVisibility(this.bodyBone, getCurrentCosmetic().getBody() != null);
|
||||
this.setBoneVisibility(this.leftArmBone, getCurrentCosmetic().getLeftArm() != null);
|
||||
this.setBoneVisibility(this.rightArmBone, getCurrentCosmetic().getRightArm() != null);
|
||||
this.setBoneVisibility(this.leftLegBone, getCurrentCosmetic().getLeftLeg() != null);
|
||||
this.setBoneVisibility(this.rightLegBone, getCurrentCosmetic().getRightLeg() != null);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a specific bone (and its child-bones) to visible or not
|
||||
* @param boneName The name of the bone
|
||||
* @param isVisible Whether the bone should be visible
|
||||
*/
|
||||
protected void setBoneVisibility(String boneName, boolean isVisible) {
|
||||
if (boneName == null)
|
||||
return;
|
||||
|
||||
this.modelProvider.getBone(boneName).setHidden(!isVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link CosmeticArmorRenderer#setBoneVisibility(String, boolean)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
protected IBone getAndHideBone(String boneName) {
|
||||
setBoneVisibility(boneName, false);
|
||||
|
||||
return this.modelProvider.getBone(boneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link IGeoRenderer#getInstanceId(Object)}<br>
|
||||
* Remove in 1.20+
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public Integer getUniqueID(CosmeticItem animatable) {
|
||||
return getInstanceId(animatable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstanceId(CosmeticItem animatable) {
|
||||
return Objects.hash(this.currentArmorItem.getCosmetic().getID(), this.entityLiving.getUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentRTB(VertexConsumerProvider bufferSource) {
|
||||
this.rtb = bufferSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumerProvider getCurrentRTB() {
|
||||
return this.rtb;
|
||||
}
|
||||
|
||||
public ICosmetic getCurrentCosmetic() {
|
||||
return this.currentArmorItem.getCosmetic();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package net.anvilcraft.ntx4core.cosmetics;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.model.ModelPart;
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.PlayerEntityRenderer;
|
||||
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
|
||||
import net.minecraft.client.render.entity.model.BipedEntityModel;
|
||||
import net.minecraft.client.render.entity.model.PlayerEntityModel;
|
||||
import net.minecraft.client.render.item.ItemRenderer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
|
||||
public class CosmeticFeatureRenderer extends ArmorFeatureRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>, BipedEntityModel<AbstractClientPlayerEntity>> {
|
||||
|
||||
private static final Map<ICosmetic, CosmeticItem> modelCache = new HashMap<>();
|
||||
private static CosmeticArmorRenderer cosmeticRenderer = null;
|
||||
PlayerEntityRenderer renderer;
|
||||
String skin;
|
||||
|
||||
public CosmeticFeatureRenderer(PlayerEntityRenderer renderer, String skin) {
|
||||
super(renderer, null, null);
|
||||
this.renderer = renderer;
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack matrix, VertexConsumerProvider buffer, int light, AbstractClientPlayerEntity player, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch) {
|
||||
for (ICosmetic c : CosmeticsManager.getCosmeticsForPlayer(player.getUuid())) {
|
||||
if (c.readyToRender()) this.renderCosmetic(matrix, buffer, player, light, c, partialTicks);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderCosmetic(MatrixStack matrix, VertexConsumerProvider buffer, AbstractClientPlayerEntity player, int light, ICosmetic cosmetic, float partialTicks) {
|
||||
if (cosmeticRenderer == null) cosmeticRenderer = new CosmeticArmorRenderer();
|
||||
if (!modelCache.containsKey(cosmetic)) modelCache.put(cosmetic, new CosmeticItem(cosmetic));
|
||||
CosmeticItem item = modelCache.get(cosmetic);
|
||||
copyRotations(this.renderer.getModel(), cosmeticRenderer);
|
||||
cosmeticRenderer.applyEntityStats(this.renderer.getModel());
|
||||
cosmeticRenderer.setCurrentItem(player, item);
|
||||
cosmeticRenderer.filterBones();
|
||||
|
||||
VertexConsumer vertex = ItemRenderer.getArmorGlintConsumer(buffer, RenderLayer.getArmorCutoutNoCull(cosmetic.getTextureLocation()), false, false);
|
||||
cosmeticRenderer.render(partialTicks, matrix, vertex, light);
|
||||
}
|
||||
|
||||
private static void copyRotations(BipedEntityModel<?> from, BipedEntityModel<?> to) {
|
||||
copyRotations(from.head, to.head);
|
||||
copyRotations(from.hat, to.hat);
|
||||
copyRotations(from.body, to.body);
|
||||
copyRotations(from.leftArm, to.leftArm);
|
||||
copyRotations(from.rightArm, to.rightArm);
|
||||
copyRotations(from.rightLeg, to.rightLeg);
|
||||
copyRotations(from.leftLeg, to.leftLeg);
|
||||
}
|
||||
|
||||
private static void copyRotations(ModelPart from, ModelPart to) {
|
||||
to.copyTransform(from);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package net.anvilcraft.ntx4core.cosmetics;
|
||||
|
||||
import software.bernie.geckolib3.core.IAnimatable;
|
||||
import software.bernie.geckolib3.core.PlayState;
|
||||
import software.bernie.geckolib3.core.builder.AnimationBuilder;
|
||||
import software.bernie.geckolib3.core.controller.AnimationController;
|
||||
import software.bernie.geckolib3.core.event.predicate.AnimationEvent;
|
||||
import software.bernie.geckolib3.core.manager.AnimationData;
|
||||
import software.bernie.geckolib3.core.manager.AnimationFactory;
|
||||
import software.bernie.geckolib3.util.GeckoLibUtil;
|
||||
|
||||
public class CosmeticItem implements IAnimatable {
|
||||
|
||||
private ICosmetic cosmetic = null;
|
||||
private AnimationBuilder animationBuilder = new AnimationBuilder();
|
||||
|
||||
public CosmeticItem(ICosmetic cosmetic) {
|
||||
this.cosmetic = cosmetic;
|
||||
this.cosmetic.addAnimations(animationBuilder);
|
||||
}
|
||||
|
||||
private <P extends IAnimatable> PlayState predicate(AnimationEvent<P> event) {
|
||||
event.getController().transitionLengthTicks = 0;
|
||||
event.getController().setAnimation(animationBuilder);
|
||||
return PlayState.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimationData data) {
|
||||
data.addAnimationController(new AnimationController<>(this, "controller", 20, this::predicate));
|
||||
}
|
||||
|
||||
private final AnimationFactory factory = GeckoLibUtil.createFactory(this);
|
||||
|
||||
@Override
|
||||
public AnimationFactory getFactory() {
|
||||
return this.factory;
|
||||
}
|
||||
|
||||
public ICosmetic getCosmetic() {
|
||||
return this.cosmetic;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package net.anvilcraft.ntx4core.cosmetics;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
import software.bernie.geckolib3.model.AnimatedGeoModel;
|
||||
|
||||
public class CosmeticModel extends AnimatedGeoModel<CosmeticItem> {
|
||||
|
||||
@Override
|
||||
public Identifier getAnimationFileLocation(CosmeticItem animatable) {
|
||||
return animatable.getCosmetic().getAnimationFileLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getModelLocation(CosmeticItem animatable) {
|
||||
return animatable.getCosmetic().getModelLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getTextureLocation(CosmeticItem animatable) {
|
||||
return animatable.getCosmetic().getTextureLocation();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package net.anvilcraft.ntx4core.cosmetics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CosmeticsManager {
|
||||
|
||||
private static List<ICosmeticProvider> providers = new ArrayList<>();
|
||||
private static Map<UUID, List<ICosmetic>> cosmeticCache = new HashMap<>();
|
||||
private static Set<UUID> activePlayers = new HashSet<>();
|
||||
|
||||
private static void refresh() {
|
||||
boolean doRefresh = false;
|
||||
for (ICosmeticProvider provider : providers) {
|
||||
doRefresh = doRefresh || provider.requestsRefresh();
|
||||
}
|
||||
if (!doRefresh) return;
|
||||
cosmeticCache.clear();
|
||||
for (UUID uuid : activePlayers) {
|
||||
loadPlayer(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadPlayer(UUID player) {
|
||||
if (cosmeticCache.containsKey(player)) return;
|
||||
cosmeticCache.put(player, new ArrayList<>());
|
||||
List<ICosmetic> cosmetics = cosmeticCache.get(player);
|
||||
for (ICosmeticProvider provider : providers) {
|
||||
provider.addCosmetics(player, (cosmetic) -> cosmetics.add(cosmetic));
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerProvider(ICosmeticProvider provider) {
|
||||
providers.add(provider);
|
||||
}
|
||||
|
||||
protected static List<ICosmetic> getCosmeticsForPlayer(UUID uuid) {
|
||||
if (!activePlayers.contains(uuid)) {
|
||||
activePlayers.add(uuid);
|
||||
loadPlayer(uuid);
|
||||
}
|
||||
refresh();
|
||||
return cosmeticCache.get(uuid);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package net.anvilcraft.ntx4core.cosmetics;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
import software.bernie.geckolib3.core.builder.AnimationBuilder;
|
||||
|
||||
public interface ICosmetic {
|
||||
|
||||
Identifier getAnimationFileLocation();
|
||||
|
||||
Identifier getModelLocation();
|
||||
|
||||
Identifier getTextureLocation();
|
||||
|
||||
default String getHead() {
|
||||
return null; //head
|
||||
}
|
||||
|
||||
default String getBody() {
|
||||
return null; //body
|
||||
}
|
||||
|
||||
default String getLeftArm() {
|
||||
return null; //arm_left
|
||||
}
|
||||
|
||||
default String getRightArm() {
|
||||
return null; //arm_right
|
||||
}
|
||||
|
||||
default String getLeftLeg() {
|
||||
return null; //leg_left
|
||||
}
|
||||
|
||||
default String getRightLeg() {
|
||||
return null; //leg_right
|
||||
}
|
||||
|
||||
void addAnimations(AnimationBuilder builder);
|
||||
|
||||
default boolean readyToRender() {
|
||||
return true;
|
||||
}
|
||||
|
||||
Identifier getID();
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package net.anvilcraft.ntx4core.cosmetics;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface ICosmeticProvider {
|
||||
|
||||
boolean requestsRefresh();
|
||||
|
||||
void addCosmetics(UUID player, Consumer<ICosmetic> cosmeticAdder);
|
||||
|
||||
}
|
|
@ -32,3 +32,10 @@ mandatory = true
|
|||
versionRange = "[1.18.2]"
|
||||
ordering = "NONE"
|
||||
side = "BOTH"
|
||||
|
||||
[[dependencies.ntx4core]]
|
||||
modId = "geckolib3"
|
||||
mandatory = true
|
||||
versionRange = "[3.0.57,)"
|
||||
ordering = "NONE"
|
||||
side = "BOTH"
|
Loading…
Reference in a new issue