Compare commits

..

4 commits

Author SHA1 Message Date
Sollace
57754d8dec
Change christmas check to include the 24th - 26th Closes #299 2025-01-05 12:28:59 +01:00
Sollace
ec8f92e151
Update hdskins
# Conflicts:
#	gradle.properties
2024-12-17 17:59:46 +01:00
Sollace
e21ce62631
Merge branch '1.20.1' into 1.20.2 2024-07-31 15:13:32 +02:00
Sollace
c78cf32bd4
Fix build 2024-07-31 15:13:14 +02:00
240 changed files with 4354 additions and 5387 deletions

View file

@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: Sollace
---
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Steps to reproduce**
Help us figure out what you did to get this issue
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Version Information:**
- Minecraft Version: [e.g. 1.20.5]
- Mine Little Pony Version:
**Mod Loader:**
- [ ] Fabric
- [ ] Quilt
- [ ] Neoforge (with connector)
**Client/Server Logs**
If applicable, add log files by uploading them as attachments or put them below.
<details>
```
**Paste logs here**
```
</details>

View file

@ -1,26 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: Sollace
---
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Version Information:**
- Minecraft Version: [e.g. 1.20.5]
- Mine Little Pony Version:
**Mod Loader:**
- [ ] Fabric
- [ ] Quilt
- [ ] Neoforge (with connector)

View file

@ -12,7 +12,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v1
with:
java-version: 21
java-version: 17
- name: Build Gradle
uses: eskatos/gradle-command-action@v1
with:

View file

@ -12,7 +12,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v1
with:
java-version: 21
java-version: 17
- name: Build Gradle
uses: eskatos/gradle-command-action@v1
with:

View file

@ -16,7 +16,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v1
with:
java-version: 21
java-version: 17
- name: Publish Maven Jar
env:
ACCESS_KEY: ${{ secrets.ACCESS_KEY }}

8
.gitmodules vendored
View file

@ -2,11 +2,3 @@
path = skins
url = https://github.com/MineLittlePony/Community-Skin-Pack.git
branch = master
[submodule "plugins/ShowMeYourPonies"]
path = plugins/ShowMeYourPonies
url = https://github.com/MineLittlePony/ShowMeYourPonies.git
branch = 1.20.5
[submodule "plugins/Sockies"]
path = plugins/Sockies
url = https://github.com/MineLittlePony/Sockies.git
branch = 1.21

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -4,7 +4,7 @@ buildscript {
}
}
plugins {
id 'fabric-loom' version '1.7-SNAPSHOT'
id 'fabric-loom' version '1.5-SNAPSHOT'
id 'maven-publish'
id 'com.modrinth.minotaur' version '2.+'
id 'org.ajoberstar.reckon' version '0.13.1'
@ -13,13 +13,13 @@ apply plugin: 'io.github.dexman545.outlet'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
languageVersion = JavaLanguageVersion.of(17)
}
withSourcesJar()
}
outlet.allowSnapshotsForProject = false
outlet.mcVersionRange = ">=${project.minecraft_version}"
outlet.mcVersionRange = project.minecraft_version_range
group = project.group
description = project.displayname
@ -47,8 +47,6 @@ dependencies {
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modApi "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "com.ptsmods:devlogin:3.4.1"
modApi fabricApi.module("fabric-api-base", project.fabric_version)
modApi fabricApi.module("fabric-lifecycle-events-v1", project.fabric_version)
modApi fabricApi.module("fabric-resource-loader-v0", project.fabric_version)
@ -125,10 +123,9 @@ modrinth {
gameVersions.add ver
}
dependencies {
required.project 'P7dR8mSH' // Fabric API
required.project '9aNz8Zqn' // Kirin
optional.project 'FzE9gshV' // HD Skins
optional.project 'h9pJxJR9' // Big Pony
required.project 'P7dR8mSH'
optional.project 'FzE9gshV'
optional.project 'h9pJxJR9'
}
}
@ -177,4 +174,4 @@ publishing {
}
}
}
}
}

View file

@ -3,10 +3,10 @@ org.gradle.daemon=false
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.21.4
yarn_mappings=1.21.4+build.2
loader_version=0.16.9
fabric_version=0.111.0+1.21.4
minecraft_version=1.20.2
yarn_mappings=1.20.2+build.4
loader_version=0.15.1
fabric_version=0.91.1+1.20.2
# Mod Properties
group=com.minelittlepony
@ -15,12 +15,12 @@ org.gradle.daemon=false
description=Mine Little Pony turns players and mobs into ponies. Press F9 ingame to access settings.
# Publishing
minecraft_version_range=>=1.21.3 <1.21.4
minecraft_version_range=>=1.20.2
modrinth_loader_type=fabric
modrinth_project_id=JBjInUXM
# Dependencies
modmenu_version=13.0.0-beta.1
kirin_version=1.20.3+1.21.4
hd_skins_version=6.14.2+1.21.4
mson_version=1.11.1+1.21.4
modmenu_version=8.0.0
kirin_version=1.16.1+1.20.2
hd_skins_version=6.12.4+1.20.2
mson_version=1.9.3+1.20.2

View file

@ -1,5 +1,5 @@
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.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

@ -1 +0,0 @@
Subproject commit 1ae97d064e7579d94a93f2784473dc7ce0e1e670

@ -1 +0,0 @@
Subproject commit 31e72eab1b764506c55f00881e05aca513ea7074

View file

@ -1,16 +1,13 @@
package com.minelittlepony.api.config;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import com.google.gson.GsonBuilder;
import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.common.client.gui.VisibilityMode;
import com.minelittlepony.common.util.GamePaths;
import com.minelittlepony.common.util.settings.*;
import java.nio.file.Path;
import java.util.HashSet;
/**
* Storage container for MineLP client settings.
@ -57,6 +54,11 @@ public class PonyConfig extends Config {
.addComment("Helps to prevent entities from vanishing when they're in long stacks");
public final Setting<Boolean> horsieMode = value("settings", "horsieMode", false)
.addComment("Enables the alternative horsey models from the April Fools 2023 update");
public final Setting<Boolean> mixedHumanSkins = value("settings", "dualSkinMode", false)
.addComment("(Experimental) Use priority to decide between displaying the HDSkins' texture, or vanilla mojang server skin")
.addComment("(Experimental) eg. On pony level = HUMANS")
.addComment("(Experimental) Any time both skins resolve to the same race (eg. on pony-level HUMANS, or if both are ponies)")
.addComment("(Experimental) the skin with the highest priority will be chosen.");;
public final Setting<SizePreset> sizeOverride = value("debug", "sizeOverride", SizePreset.UNSET)
.addComment("Overrides pony sizes")
@ -83,13 +85,8 @@ public class PonyConfig extends Config {
.addComment("ON - always show")
.addComment("OFF - never show");
public final Setting<HashSet<Identifier>> forwardHoldingItems = value("customisation", "forwardHoldingItems", HashSet::new, Identifier.class)
.addComment("Contains a list of item ids that should preserve orientation")
.addComment("when held in a unicorn's magical aura in first person");
public PonyConfig(Path path) {
super(new HeirarchicalJsonConfigAdapter(new GsonBuilder()
.registerTypeAdapter(Identifier.class, new ToStringAdapter<>(Identifier::toString, Identifier::of))), path);
super(HEIRARCHICAL_JSON_ADAPTER, path);
instance = this;
}
@ -135,12 +132,12 @@ public class PonyConfig extends Config {
*/
public static Race getEffectiveRace(Race race) {
Race override = getInstance().raceOverride.get();
Race override = instance.raceOverride.get();
if (override != Race.HUMAN) {
return override;
}
if (getInstance().ponyLevel.get() == PonyLevel.HUMANS) {
if (instance.ponyLevel.get() == PonyLevel.HUMANS) {
return Race.HUMAN;
}
@ -148,7 +145,7 @@ public class PonyConfig extends Config {
}
public static Size getEffectiveSize(Size size) {
SizePreset sz = getInstance().sizeOverride.get();
SizePreset sz = instance.sizeOverride.get();
if (sz != SizePreset.UNSET) {
return sz;

View file

@ -0,0 +1,63 @@
package com.minelittlepony.api.events;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.minelittlepony.api.pony.PonyData;
@Environment(EnvType.CLIENT)
public class Channel {
private static final Identifier CLIENT_PONY_DATA = new Identifier("minelittlepony", "pony_data");
private static final Identifier REQUEST_PONY_DATA = new Identifier("minelittlepony", "request_pony_data");
private static final Logger LOGGER = LogManager.getLogger("MineLittlePony:Networking");
private static boolean registered;
public static void bootstrap() {
ClientLoginConnectionEvents.INIT.register((handler, client) -> {
registered = false;
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
LOGGER.info("Sending consent packet to " + handler.getPlayer().getName().getString());
sender.sendPacket(REQUEST_PONY_DATA, PacketByteBufs.empty());
});
ClientPlayNetworking.registerGlobalReceiver(REQUEST_PONY_DATA, (client, handler, ignored, sender) -> {
registered = true;
LOGGER.info("Server has just consented");
});
ServerPlayNetworking.registerGlobalReceiver(CLIENT_PONY_DATA, (server, player, ignore, buffer, ignore2) -> {
PonyData packet = MsgPonyData.read(buffer);
server.execute(() -> {
PonyDataCallback.EVENT.invoker().onPonyDataAvailable(player, packet, EnvType.SERVER);
});
});
}
public static boolean isRegistered() {
return registered;
}
public static boolean broadcastPonyData(PonyData packet) {
if (!isRegistered()) {
return false;
}
if (FabricLoader.getInstance().getEnvironmentType() != EnvType.CLIENT) {
throw new RuntimeException("Client packet send called by the server");
}
ClientPlayNetworking.send(CLIENT_PONY_DATA, MsgPonyData.write(packet, PacketByteBufs.create()));
return true;
}
}

View file

@ -1,46 +0,0 @@
package com.minelittlepony.api.events;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.loader.api.FabricLoader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.minelittlepony.api.pony.PonyData;
@Environment(EnvType.CLIENT)
public class ClientChannel {
private static final Logger LOGGER = LogManager.getLogger("MineLittlePony:Networking");
private static boolean registered;
public static void bootstrap() {
ClientLoginConnectionEvents.INIT.register((handler, client) -> {
registered = false;
});
ClientPlayNetworking.registerGlobalReceiver(CommonChannel.PonyDataRequest.ID, (packet, context) -> {
registered = true;
LOGGER.info("Server has just consented");
});
}
public static boolean isRegistered() {
return registered;
}
public static boolean broadcastPonyData(PonyData packet) {
if (!isRegistered()) {
return false;
}
if (FabricLoader.getInstance().getEnvironmentType() != EnvType.CLIENT) {
throw new RuntimeException("Client packet send called by the server");
}
ClientPlayNetworking.send(new CommonChannel.PonyDataPayload(packet));
return true;
}
}

View file

@ -1,58 +0,0 @@
package com.minelittlepony.api.events;
import net.fabricmc.api.EnvType;
import net.fabricmc.fabric.api.networking.v1.*;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.packet.CustomPayload;
import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.minelittlepony.api.pony.PonyData;
public class CommonChannel {
private static final Logger LOGGER = LogManager.getLogger("MineLittlePony:Networking");
public static void bootstrap() {
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
LOGGER.info("Sending consent packet to " + handler.getPlayer().getName().getString());
sender.sendPacket(PonyDataRequest.INSTANCE);
});
PayloadTypeRegistry.playS2C().register(PonyDataRequest.ID, PonyDataRequest.CODEC);
PayloadTypeRegistry.playS2C().register(PonyDataPayload.ID, PonyDataPayload.CODEC);
PayloadTypeRegistry.playC2S().register(PonyDataPayload.ID, PonyDataPayload.CODEC);
ServerPlayNetworking.registerGlobalReceiver(PonyDataPayload.ID, (packet, context) -> {
context.player().server.execute(() -> {
PonyDataCallback.EVENT.invoker().onPonyDataAvailable(context.player(), packet.data(), EnvType.SERVER);
});
});
}
record PonyDataPayload(PonyData data) implements CustomPayload {
public static final Id<PonyDataPayload> ID = new Id<>(Identifier.of("minelittlepony", "pony_data"));
public static final PacketCodec<PacketByteBuf, PonyDataPayload> CODEC = CustomPayload.codecOf(
(p, buffer) -> MsgPonyData.write(p.data(), buffer),
buffer -> new PonyDataPayload(MsgPonyData.read(buffer))
);
@Override
public Id<PonyDataPayload> getId() {
return ID;
}
}
record PonyDataRequest() implements CustomPayload {
public static final PonyDataRequest INSTANCE = new PonyDataRequest();
public static final Id<PonyDataRequest> ID = new Id<>(Identifier.of("minelittlepony", "request_pony_data"));
public static final PacketCodec<PacketByteBuf, PonyDataRequest> CODEC = PacketCodec.unit(INSTANCE);
@Override
public Id<? extends CustomPayload> getId() {
return ID;
}
}
}

View file

@ -2,22 +2,18 @@ package com.minelittlepony.api.events;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.entity.Entity;
import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.api.model.ModelAttributes;
/**
* Event triggered when a pony model's state is being evaluated.
* <p>
* Subscribers have the option to read the pony model's attributes or modify them if neccessary to
* allow for custom animations.
*/
public interface PonyModelPrepareCallback {
Event<PonyModelPrepareCallback> EVENT = EventFactory.createArrayBacked(PonyModelPrepareCallback.class, listeners -> (entity, model, mode) -> {
for (PonyModelPrepareCallback event : listeners) {
event.onPonyModelPrepared(entity, model, mode);
}
});
void onPonyModelPrepared(ModelAttributes attributes, PonyModel<?> model, ModelAttributes.Mode mode);
void onPonyModelPrepared(Entity entity, PonyModel<?> model, ModelAttributes.Mode mode);
}

View file

@ -1,29 +0,0 @@
package com.minelittlepony.api.events;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.pony.Pony;
/**
* Event for mods that want to replace the skin being used by a pony.
*/
public interface PonySkinResolver {
Event<PonySkinResolver> EVENT = EventFactory.createArrayBacked(PonySkinResolver.class, listeners -> (entity, pony, result) -> {
for (PonySkinResolver event : listeners) {
result = event.onPonySkinResolving(entity, pony, result);
}
return result;
});
@Nullable
Identifier onPonySkinResolving(Entity entity, PonyLookup ponyLookup, @Nullable Identifier previousResult);
interface PonyLookup {
Pony getPony(Identifier skin);
}
}

View file

@ -0,0 +1,17 @@
package com.minelittlepony.api.model;
import net.minecraft.entity.LivingEntity;
public interface HornedPonyModel<T extends LivingEntity> extends PonyModel<T> {
/**
* Returns true if this model is being applied to a race that can use magic.
*/
default boolean hasMagic() {
return getRace().hasHorn() && getAttributes().metadata.glowColor() != 0;
}
/**
* Returns true if this model is currently using magic (horn is lit).
*/
boolean isCasting();
}

View file

@ -30,25 +30,26 @@ public final class MobPosingHelper {
arm.roll = cos;
}
public static void rotateUndeadArms(PonyModel.AttributedHolder attributes, PonyModel<?> model, float limbAngle, float ticks) {
if (islookAngleRight(limbAngle)) {
ModelPart rightArm = model.getForeLeg(Arm.RIGHT);
rotateArmHolding(rightArm, 1, attributes.getSwingAmount(), ticks);
if (attributes.getAttributes().isSitting) {
public static void rotateUndeadArms(PonyModel<?> model, float move, float ticks) {
ModelPart leftArm = model.getForeLeg(Arm.LEFT);
ModelPart rightArm = model.getForeLeg(Arm.RIGHT);
if (islookAngleRight(move)) {
rotateArmHolding(rightArm, 1, model.getSwingAmount(), ticks);
if (model.getAttributes().isSitting) {
rightArm.pitch += 0.6F;
}
PartUtil.shift(rightArm, 0.5F, 1.5F, 3);
} else {
ModelPart leftArm = model.getForeLeg(Arm.LEFT);
rotateArmHolding(leftArm, -1, attributes.getSwingAmount(), ticks);
if (attributes.getAttributes().isSitting) {
rotateArmHolding(leftArm, -1, model.getSwingAmount(), ticks);
if (model.getAttributes().isSitting) {
leftArm.pitch += 0.6F;
}
PartUtil.shift(leftArm, -0.5F, 1.5F, 3);
}
}
public static boolean islookAngleRight(float limbAngle) {
return MathHelper.sin(limbAngle / 20) < 0;
public static boolean islookAngleRight(float move) {
return MathHelper.sin(move / 20) < 0;
}
}

View file

@ -2,7 +2,6 @@ package com.minelittlepony.api.model;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.pony.*;
import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.common.util.animation.Interpolator;
import com.minelittlepony.util.MathUtil;
@ -62,7 +61,7 @@ public class ModelAttributes {
*/
public boolean isLeftHanded;
/**
* True if the model is riding on the back of another pony.
* True if the model is sitting as in boats.
*/
public boolean isRidingInteractive;
/**
@ -74,11 +73,6 @@ public class ModelAttributes {
*/
public boolean isHorsey;
/**
* Flag indicating whether the pony is a player
*/
public boolean isPlayer;
/**
* Vertical pitch whilst flying.
*/
@ -111,11 +105,6 @@ public class ModelAttributes {
*/
public float wingAngle;
/**
* Flag to indicate whether the wings are open or shut
*/
public boolean wingsSpread;
/**
* Contains a list of additional skins available for rendering.
*/
@ -126,11 +115,8 @@ public class ModelAttributes {
*/
public PonyData metadata = PonyData.NULL;
public Size size = SizePreset.NORMAL;
public Arm mainArm;
public Hand activeHand;
@Deprecated
public ItemStack heldStack = ItemStack.EMPTY;
public int itemUseTime;
@ -144,12 +130,11 @@ public class ModelAttributes {
isGoingFast = (isFlying && model instanceof WingedPonyModel) || isGliding;
isGoingFast &= zMotion > 0.4F;
isGoingFast |= entity.isUsingRiptide();
isGoingFast |= entity.isGliding();
isGoingFast |= entity.isFallFlying();
motionLerp = MathUtil.clampLimit(zMotion * 30, 1);
wingAngle = calcWingRotationFactor(ticks);
wingsSpread = (isSwimming || isFlying || isCrouching) && (PonyConfig.getInstance().flappyElytras.get() || !isGliding);
}
private float calcWingRotationFactor(float ticks) {
@ -163,21 +148,18 @@ public class ModelAttributes {
}
public void updateLivingState(LivingEntity entity, Pony pony, Mode mode) {
metadata = pony.metadata();
size = entity.isBaby() ? SizePreset.FOAL : pony.size();
isPlayer = entity instanceof PlayerEntity;
visualHeight = entity.getHeight() + 0.125F;
isSitting = PonyPosture.isSitting(entity);
isSleeping = entity.isAlive() && entity.isSleeping();;
isLyingDown = isSleeping;
if (isPlayer) {
if (entity instanceof PlayerEntity) {
boolean moving = entity.getVelocity().multiply(1, 0, 1).length() == 0 && entity.isSneaking();
isLyingDown |= getMainInterpolator().interpolate("lyingDown", moving ? 10 : 0, 200) >= 9;
}
isCrouching = !isLyingDown && !isSitting && mode == Mode.THIRD_PERSON && PonyPosture.isCrouching(pony, entity);
isFlying = !isLyingDown && mode == Mode.THIRD_PERSON && PonyPosture.isFlying(entity);
isGliding = entity.isGliding();
isGliding = entity.isFallFlying();
isSwimming = mode == Mode.THIRD_PERSON && PonyPosture.isSwimming(entity);
isSwimmingRotated = isSwimming;
isRiptide = entity.isUsingRiptide();
@ -187,7 +169,7 @@ public class ModelAttributes {
}
isLeftHanded = entity.getMainArm() == Arm.LEFT;
isHorsey = PonyConfig.getInstance().horsieMode.get();
featureSkins = SkinsProxy.getInstance().getAvailableSkins(entity);
featureSkins = SkinsProxy.instance.getAvailableSkins(entity);
mainArm = entity.getMainArm();
activeHand = entity.getActiveHand();
itemUseTime = entity.getItemUseTimeLeft();
@ -197,31 +179,12 @@ public class ModelAttributes {
return Interpolator.linear(interpolatorId);
}
public UUID getEntityId() {
return interpolatorId;
}
public boolean shouldLiftArm(ArmPose pose, ArmPose complement, float sigma) {
return pose != ArmPose.EMPTY
&& (pose != complement || sigma == (isLeftHanded ? 1 : -1))
&& (complement != ArmPose.BLOCK && complement != ArmPose.CROSSBOW_HOLD);
}
/**
* Tests if this model is wearing the given piece of gear.
*/
public boolean isWearing(Wearable wearable) {
return isEmbedded(wearable) || featureSkins.contains(wearable.getId());
}
/**
* Tests if the chosen piece of gear is sourcing its texture from the main skin.
* i.e. Used to change wing rendering when using saddlebags.
*/
public boolean isEmbedded(Wearable wearable) {
return metadata.gear().matches(wearable);
}
public enum Mode {
FIRST_PERSON,
THIRD_PERSON,

View file

@ -2,10 +2,13 @@ package com.minelittlepony.api.model;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.ModelWithArms;
import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose;
import net.minecraft.util.Arm;
public interface ModelWithHooves extends ModelWithArms {
ModelPart getForeLeg(Arm side);
ModelPart getHindLeg(Arm side);
ArmPose getArmPoseForSide(Arm side);
}

View file

@ -1,42 +1,57 @@
package com.minelittlepony.api.model;
import net.minecraft.client.render.entity.equipment.EquipmentModel;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Util;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.client.model.AbstractPonyModel;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.client.model.PlayerModelKey;
import com.minelittlepony.client.model.armour.*;
import com.minelittlepony.mson.api.ModelKey;
import com.minelittlepony.mson.api.MsonModel;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Container class for the various models and their associated piece of armour.
*/
public record Models<M extends PonyModel<?>> (
Function<ModelKey<AbstractPonyModel<?>>, AbstractPonyModel<?>> armor,
M body
) {
public class Models<T extends LivingEntity, M extends PonyModel<?>> {
@Nullable
private final MsonModel.Factory<PonyArmourModel<T>> armorFactory;
private final Map<ModelKey<PonyArmourModel<?>>, PonyArmourModel<T>> armor = new HashMap<>();
public Models(PlayerModelKey<? super M> playerModelKey, boolean slimArms, @Nullable Consumer<M> initializer) {
this(Util.memoize(key -> key.createModel(playerModelKey.armorFactory())), playerModelKey.getKey(slimArms).createModel());
private final M body;
public Models(PlayerModelKey<T, ? super M> playerModelKey, boolean slimArms, @Nullable Consumer<M> initializer) {
this.armorFactory = playerModelKey.armorFactory();
this.body = playerModelKey.getKey(slimArms).createModel();
if (initializer != null) {
initializer.accept(body);
initializer.accept(this.body);
}
}
public Models(ModelKey<M> key) {
this(Util.memoize(k -> k.createModel()), key.createModel());
this.armorFactory = null;
this.body = key.createModel();
}
public Optional<AbstractPonyModel<?>> getArmourModel(ItemStack stack, EquipmentModel.LayerType layerType, ArmourVariant variant) {
return ArmorModelRegistry.getModelKey(stack.getItem(), layerType)
.or(() -> variant.getDefaultModel(layerType))
.map(armor);
public M body() {
return body;
}
public Optional<PonyArmourModel<T>> getArmourModel(ItemStack stack, ArmourLayer layer, ArmourVariant variant) {
return ArmorModelRegistry.getModelKey(stack.getItem(), layer).or(() -> variant.getDefaultModel(layer).filter(l -> stack.getItem() instanceof ArmorItem))
.map(key -> armor.computeIfAbsent(key, k -> {
return armorFactory == null ? k.createModel() : k.createModel(armorFactory);
}));
}
public Models<T, M> applyMetadata(PonyData meta) {
body.setMetadata(meta);
armor.values().forEach(a -> a.setMetadata(meta));
return this;
}
}

View file

@ -2,30 +2,84 @@ package com.minelittlepony.api.model;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.*;
import net.minecraft.client.render.entity.state.EntityRenderState;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.mson.api.MsonModel;
public interface PonyModel<T extends EntityRenderState & PonyModel.AttributedHolder> extends MsonModel, ModelWithHooves, ModelWithHead {
public interface PonyModel<T extends LivingEntity> extends MsonModel, ModelWithHooves, ModelWithHat, ModelWithHead {
void copyAttributes(BipedEntityModel<T> other);
void updateLivingState(T entity, Pony pony, ModelAttributes.Mode mode);
ModelPart getBodyPart(BodyPart part);
/**
* Applies a transform particular to a certain body part.
*/
void transform(T state, BodyPart part, MatrixStack stack);
void transform(BodyPart part, MatrixStack stack);
default float getWobbleAmplitude(T state) {
return 1;
/**
* Gets the transitive properties of this model.
*/
ModelAttributes getAttributes();
/**
* Sets the pony metadata object associated with this model.
*/
void setMetadata(PonyData meta);
/**
* Gets the active scaling profile used to lay out this model's parts.
*/
default Size getSize() {
return PonyConfig.getEffectiveSize(getAttributes().metadata.size());
}
public interface AttributedHolder {
ModelAttributes getAttributes();
Race getRace();
float getSwingAmount();
default Race getRace() {
return PonyConfig.getEffectiveRace(getAttributes().metadata.race());
}
/**
* Gets the current leg swing amount.
*/
float getSwingAmount();
/**
* Gets the step wobble used for various hair bits and animations.
*/
default float getWobbleAmount() {
if (getSwingAmount() <= 0) {
return 0;
}
return MathHelper.sin(MathHelper.sqrt(getSwingAmount()) * MathHelper.PI * 2) * 0.04F;
}
/**
* Gets the y-offset applied to entities riding this one.
*/
float getRiderYOffset();
/**
* Tests if this model is wearing the given piece of gear.
*/
default boolean isWearing(Wearable wearable) {
return isEmbedded(wearable) || getAttributes().featureSkins.contains(wearable.getId());
}
/**
* Tests if the chosen piece of gear is sourcing its texture from the main skin.
* i.e. Used to change wing rendering when using saddlebags.
*/
default boolean isEmbedded(Wearable wearable) {
return getAttributes().metadata.gear().matches(wearable);
}
}

View file

@ -0,0 +1,117 @@
package com.minelittlepony.api.model;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.ModelWithArms;
import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm;
import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.api.pony.meta.Size;
import com.minelittlepony.mson.api.ModelView;
import com.minelittlepony.mson.api.model.BoxBuilder.RenderLayerSetter;
public interface PonyModelMixin<T extends LivingEntity, M extends PonyModel<T>> extends PonyModel<T> {
M mixin();
@Override
default void init(ModelView context) {
mixin().init(context);
if (mixin() instanceof RenderLayerSetter && this instanceof RenderLayerSetter) {
((RenderLayerSetter)this).setRenderLayerFactory(((RenderLayerSetter)mixin()).getRenderLayerFactory());
}
}
@Override
default void updateLivingState(T entity, Pony pony, ModelAttributes.Mode mode) {
mixin().updateLivingState(entity, pony, mode);
}
@Override
default void copyAttributes(BipedEntityModel<T> other) {
mixin().copyAttributes(other);
}
@Override
default void transform(BodyPart part, MatrixStack stack) {
mixin().transform(part, stack);
}
@Override
default ModelAttributes getAttributes() {
return mixin().getAttributes();
}
@Override
default Size getSize() {
return mixin().getSize();
}
@Override
default void setMetadata(PonyData meta) {
mixin().setMetadata(meta);
}
@Override
default float getSwingAmount() {
return mixin().getSwingAmount();
}
@Override
default float getWobbleAmount() {
return mixin().getWobbleAmount();
}
@Override
default float getRiderYOffset() {
return mixin().getRiderYOffset();
}
@Override
default ModelPart getForeLeg(Arm side) {
return mixin().getForeLeg(side);
}
@Override
default ModelPart getHindLeg(Arm side) {
return mixin().getHindLeg(side);
}
@Override
default ArmPose getArmPoseForSide(Arm side) {
return mixin().getArmPoseForSide(side);
}
@Override
default void setArmAngle(Arm arm, MatrixStack stack) {
if (mixin() instanceof ModelWithArms) {
((ModelWithArms)mixin()).setArmAngle(arm, stack);
}
}
@Override
default ModelPart getHead() {
return mixin().getHead();
}
@Override
default ModelPart getBodyPart(BodyPart part) {
return mixin().getBodyPart(part);
}
@Override
default void setHatVisible(boolean hatVisible) {
mixin().setHatVisible(hatVisible);
}
interface Caster<T extends LivingEntity, M extends PonyModel<T> & HornedPonyModel<T>, ArmModel> extends PonyModelMixin<T, M>, HornedPonyModel<T> {
@Override
default boolean isCasting() {
return mixin().isCasting();
}
}
}

View file

@ -1,7 +1,7 @@
package com.minelittlepony.api.model;
import net.minecraft.util.Identifier;
public interface PreviewModel {
Identifier getForm();
boolean forceSeapony();
boolean forceNirik();
}

View file

@ -1,38 +1,25 @@
package com.minelittlepony.api.model;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.entity.state.EntityRenderState;
import net.minecraft.client.util.math.MatrixStack;
import com.minelittlepony.mson.util.RenderList;
public interface SubModel {
/**
* Sets the model's various rotation angles.
*/
default void setPartAngles(ModelAttributes attributes, float limbAngle, float limbSpeed, float bodySwing, float animationProgress) {
import java.util.function.Supplier;
public interface SubModel<T extends EntityRenderState> extends RenderList {
static <T extends EntityRenderState> RenderList toRenderList(Supplier<SubModel<? super T>> part) {
return (stack, vertices, overlay, light, color) -> part.get().renderPart(stack, vertices, overlay, light, color);
}
/**
* Renders this model component.
*/
void renderPart(MatrixStack stack, VertexConsumer vertices, int overlay, int light, int color);
@Override
default void accept(MatrixStack stack, VertexConsumer vertices, int overlay, int light, int color) {
renderPart(stack, vertices, overlay, light, color);
}
/**
* Sets the model's various rotation angles.
*/
default void setPartAngles(T state, float wobbleAmount) {
}
void renderPart(MatrixStack stack, VertexConsumer vertices, int overlay, int light, float red, float green, float blue, float alpha, ModelAttributes attributes);
/**
* Sets whether this part should be rendered.
*/
default void setVisible(boolean visible, T state) {
default void setVisible(boolean visible, ModelAttributes attributes) {
}
}

View file

@ -1,32 +1,42 @@
package com.minelittlepony.api.model;
import net.minecraft.client.render.entity.state.BipedEntityRenderState;
import net.minecraft.entity.LivingEntity;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.util.MathUtil;
public interface WingedPonyModel<T extends BipedEntityRenderState & PonyModel.AttributedHolder> extends PonyModel<T> {
public interface WingedPonyModel<T extends LivingEntity> extends PonyModel<T> {
public static final float WINGS_HALF_SPREAD_ANGLE = MathUtil.Angles._270_DEG;
public static final float WINGS_FULL_SPREAD_ANGLE = MathUtil.Angles._270_DEG + 0.4F;
public static final float WINGS_RAISED_ANGLE = 4;
/**
* Gets the wings of this pegasus/flying creature
*/
SubModel<T> getWings();
/**
* Returns true if the wings are spread.
*/
default boolean wingsAreOpen(T state) {
return state.getAttributes().wingsSpread;
default boolean wingsAreOpen() {
return (getAttributes().isSwimming || getAttributes().isFlying || getAttributes().isCrouching)
&& (PonyConfig.getInstance().flappyElytras.get() || !getAttributes().isGliding);
}
default boolean isBurdened() {
return isWearing(Wearable.SADDLE_BAGS_BOTH)
|| isWearing(Wearable.SADDLE_BAGS_LEFT)
|| isWearing(Wearable.SADDLE_BAGS_RIGHT);
}
/**
* Gets the wings of this pegasus/flying creature
*/
SubModel getWings();
/**
* Determines angle used to animate wing flaps whilst flying/swimming.
*
* @param ticks Partial render ticks
*/
default float getWingRotationFactor(T state) {
return state.getAttributes().wingAngle;
default float getWingRotationFactor(float ticks) {
return getAttributes().wingAngle;
}
}

View file

@ -6,20 +6,36 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public abstract class AbstractGearModel extends Model implements Gear {
private final List<ModelPart> parts = new ArrayList<>();
private final float stackingHeight;
public AbstractGearModel(ModelPart root, float stackingHeight) {
super(root, RenderLayer::getEntitySolid);
public AbstractGearModel(float stackingHeight) {
super(RenderLayer::getEntitySolid);
this.stackingHeight = stackingHeight;
}
public AbstractGearModel addPart(ModelPart t) {
parts.add(t);
return this;
}
@Override
public void render(MatrixStack stack, VertexConsumer vertices, int overlay, int light, int color, UUID interpolatorId) {
render(stack, vertices, overlay, light, color);
public void render(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha, UUID interpolatorId) {
render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha);
}
@Override
public void render(MatrixStack stack, VertexConsumer renderContext, int overlayUv, int lightUv, float red, float green, float blue, float alpha) {
parts.forEach(part -> {
part.render(stack, renderContext, overlayUv, lightUv, red, green, blue, alpha);
});
}
@Override

View file

@ -2,9 +2,9 @@ package com.minelittlepony.api.model.gear;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.entity.state.BipedEntityRenderState;
import net.minecraft.client.render.entity.state.EntityRenderState;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import com.minelittlepony.api.model.*;
@ -37,7 +37,7 @@ public interface Gear {
*
* @return True to render this wearable
*/
boolean canRender(PonyModel<?> model, EntityRenderState entity);
boolean canRender(PonyModel<?> model, Entity entity);
/**
* Gets the body location that this wearable appears on.
@ -62,21 +62,21 @@ public interface Gear {
*
* If you need to use the player's own skin, use {@link IRenderContext#getDefaultTexture(entity, wearable)}
*/
<S extends EntityRenderState> Identifier getTexture(S entity, Context<S, ?> context);
<T extends Entity> Identifier getTexture(T entity, Context<T, ?> context);
/**
* Gets the layer used to render this piece of gear.
*/
default <S extends EntityRenderState> RenderLayer getLayer(S entity, Context<S, ?> context) {
default <T extends Entity> RenderLayer getLayer(T entity, Context<T, ?> context) {
return RenderLayer.getEntityTranslucent(getTexture(entity, context));
}
/**
* Applies body transformations for this wearable
*/
default <S extends EntityRenderState & PonyModel.AttributedHolder> void transform(S state, PonyModel<S> model, MatrixStack matrices) {
default <M extends EntityModel<?> & PonyModel<?>> void transform(M model, MatrixStack matrices) {
BodyPart part = getGearLocation();
model.transform(state, part, matrices);
model.transform(part, matrices);
model.getBodyPart(part).rotate(matrices);
}
@ -85,14 +85,14 @@ public interface Gear {
*
* See {@link AbstractPonyMode.setRotationAndAngle} for an explanation of the various parameters.
*/
default <S extends BipedEntityRenderState & PonyModel.AttributedHolder> void pose(PonyModel<S> model, S state, boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
default void pose(PonyModel<?> model, Entity entity, boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
}
/**
* Renders this model component.
*/
void render(MatrixStack stack, VertexConsumer vertices, int overlay, int light, int color, UUID interpolatorId);
void render(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha, UUID interpolatorId);
/**
* A render context for instance of IGear.
@ -100,7 +100,7 @@ public interface Gear {
* @param <T> The type of entity being rendered.
* @param <M> The type of the entity's primary model.
*/
public interface Context<S extends EntityRenderState, M extends PonyModel<?>> {
public interface Context<T extends Entity, M extends PonyModel<?>> {
/**
* The empty context.
*/
@ -109,7 +109,7 @@ public interface Gear {
/**
* Checks whether the given wearable and gear are able to render for this specific entity and its renderer.
*/
default boolean shouldRender(M model, S entity, Wearable wearable, Gear gear) {
default boolean shouldRender(M model, T entity, Wearable wearable, Gear gear) {
return gear.canRender(model, entity);
}
@ -118,6 +118,6 @@ public interface Gear {
*
* May be the entity's own texture or a specific texture allocated for that wearable.
*/
Identifier getDefaultTexture(S entity, Wearable wearable);
Identifier getDefaultTexture(T entity, Wearable wearable);
}
}

View file

@ -1,21 +1,19 @@
package com.minelittlepony.api.model.gear;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.state.EntityRenderState;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.render.entity.state.PonyRenderState;
public class WearableGear extends AbstractGearModel {
protected final Wearable wearable;
protected final BodyPart location;
public WearableGear(ModelPart root, Wearable wearable, BodyPart location, float stackingHeight) {
super(root, stackingHeight);
public WearableGear(Wearable wearable, BodyPart location, float stackingHeight) {
super(stackingHeight);
this.wearable = wearable;
this.location = location;
}
@ -26,12 +24,12 @@ public class WearableGear extends AbstractGearModel {
}
@Override
public boolean canRender(PonyModel<?> model, EntityRenderState entity) {
return entity instanceof PonyRenderState state && state.isWearing(wearable);
public boolean canRender(PonyModel<?> model, Entity entity) {
return model.isWearing(wearable);
}
@Override
public <S extends EntityRenderState> Identifier getTexture(S entity, Context<S, ?> context) {
public <T extends Entity> Identifier getTexture(T entity, Context<T, ?> context) {
return context.getDefaultTexture(entity, wearable);
}
}

View file

@ -1,35 +1,32 @@
package com.minelittlepony.api.pony;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.util.DefaultSkinHelper;
import net.minecraft.client.util.SkinTextures;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
import com.minelittlepony.api.pony.meta.Race;
import java.util.*;
import java.util.function.Function;
@Environment(EnvType.CLIENT)
public final class DefaultPonySkinHelper {
public static final Identifier STEVE = Pony.id("textures/entity/player/wide/steve_pony.png");
public static final Identifier STEVE = new Identifier("minelittlepony", "textures/entity/player/wide/steve_pony.png");
public static final Identifier SEAPONY_SKIN_TYPE_ID = Pony.id("seapony");
public static final Identifier NIRIK_SKIN_TYPE_ID = Pony.id("nirik");
public static final Identifier SEAPONY_SKIN_TYPE_ID = new Identifier("minelp", "seapony");
public static final Identifier NIRIK_SKIN_TYPE_ID = new Identifier("minelp", "nirik");
private static final Function<SkinTextures, SkinTextures> SKINS = Util.memoize(original -> new SkinTextures(
Pony.id(original.texture().getPath().replace(".png", "_pony.png")),
null,
null,
null,
original.model(),
false
));
private static final Map<SkinTextures, SkinTextures> SKINS = new HashMap<>();
public static SkinTextures getTextures(SkinTextures original) {
return SKINS.apply(original);
return SKINS.computeIfAbsent(original, o -> {
return new SkinTextures(
new Identifier("minelittlepony", original.texture().getPath().replace(".png", "_pony.png")),
null,
null,
null,
original.model(),
false
);
});
}
public static String getModelType(UUID id) {

View file

@ -26,13 +26,6 @@ public record Pony (
return PonyManager.Instance.instance;
}
/**
* Gets an identifier for the Mine Little Pony namespace
*/
public static Identifier id(String name) {
return Identifier.of("minelittlepony", name);
}
/**
* Gets the metadata associated with this pony's model texture.
*/

View file

@ -6,8 +6,6 @@ import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ComparisonChain;
import com.minelittlepony.api.pony.meta.*;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.*;
import java.util.function.Function;
@ -72,20 +70,6 @@ public record PonyData (
private static final Function<Race, PonyData> OF_RACE = Util.memoize(race -> new PonyData(race, TailLength.FULL, TailShape.STRAIGHT, Gender.MARE, SizePreset.NORMAL, DEFAULT_MAGIC_COLOR, true, 0, Wearable.EMPTY_FLAGS));
public static final PonyData NULL = OF_RACE.apply(Race.HUMAN);
public static final Codec<PonyData> CODEC = RecordCodecBuilder.create(i -> {
return i.group(
Race.CODEC.fieldOf("race").forGetter(PonyData::race),
TailLength.CODEC.fieldOf("tailLength").forGetter(PonyData::tailLength),
TailShape.CODEC.fieldOf("tailShape").forGetter(PonyData::tailShape),
Gender.CODEC.fieldOf("gender").forGetter(PonyData::gender),
SizePreset.CODEC.xmap(s -> (Size)s, s -> (SizePreset)s).fieldOf("size").forGetter(PonyData::size),
Codec.INT.fieldOf("glowColor").forGetter(PonyData::glowColor),
Codec.BOOL.optionalFieldOf("noSkin", false).forGetter(PonyData::noSkin),
Codec.INT.optionalFieldOf("priority", 0).forGetter(PonyData::priority),
Wearable.FLAGS_CODEC.fieldOf("gear").forGetter(PonyData::gear)
).apply(i, PonyData::new);
});
public static PonyData emptyOf(Race race) {
return OF_RACE.apply(race);
}

View file

@ -1,52 +0,0 @@
package com.minelittlepony.api.pony;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.client.render.entity.PlayerPonyRenderer;
import java.util.*;
import java.util.function.Predicate;
/**
* The different forms a pony can take.
* <p>
* The default is land, which is your typical pony with four legs.
* Other options are water (seaponies that go shoop-de-doo)
* And Niriks (the burning form of kirins)
*/
@Environment(EnvType.CLIENT)
public record PonyForm(Identifier id, Predicate<PlayerEntity> shouldApply, RendererFactory<?> factory) {
public static final Identifier DEFAULT = Pony.id("land");
public static final Identifier SEAPONY = Pony.id("seapony");
public static final Identifier NIRIK = Pony.id("nirik");
public static final List<Identifier> VALUES = new ArrayList<>();
public static final Map<Identifier, PonyForm> REGISTRY = new HashMap<>();
public static void register(Identifier id, Predicate<PlayerEntity> shouldApply, RendererFactory<?> factory) {
VALUES.add(0, id);
REGISTRY.put(id, new PonyForm(id, shouldApply, factory));
}
@Nullable
public static PonyForm of(PlayerEntity player) {
for (Identifier id : VALUES) {
PonyForm form = REGISTRY.get(id);
if (form != null && form.shouldApply().test(player)) {
return form;
}
}
return null;
}
public interface RendererFactory<T extends PlayerPonyRenderer> {
T create(EntityRendererFactory.Context context, boolean slimArms);
}
}

View file

@ -7,8 +7,6 @@ import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.server.ServerPonyManager;
import java.util.Optional;
import java.util.UUID;
@ -73,6 +71,6 @@ public interface PonyManager {
interface ForcedPony {}
final class Instance {
public static PonyManager instance = new ServerPonyManager();
public static PonyManager instance;
}
}

View file

@ -7,6 +7,7 @@ import java.util.Optional;
import net.minecraft.block.BlockState;
import net.minecraft.block.StairsBlock;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.registry.tag.FluidTags;
@ -32,7 +33,7 @@ public final class PonyPosture {
Vec3d motion = entity.getVelocity();
double zMotion = Math.sqrt(motion.x * motion.x + motion.z * motion.z);
return (isFlying(entity) && pony.race().hasWings()) || entity.isGliding() & zMotion > 0.4F;
return (isFlying(entity) && pony.race().hasWings()) || entity.isFallFlying() & zMotion > 0.4F;
}
public static boolean isFlying(LivingEntity entity) {
@ -85,24 +86,39 @@ public final class PonyPosture {
return isSitting(entity) && getMountPony(entity).map(Pony::race).orElse(Race.HUMAN) != Race.HUMAN;
}
public static boolean isSeaponyModifier(LivingEntity entity) {
if (entity instanceof PreviewModel preview) {
return preview.forceSeapony();
}
return hasSeaponyForm(entity) && isPartiallySubmerged(entity);
}
public static boolean hasSeaponyForm(LivingEntity entity) {
if (entity instanceof PreviewModel preview) {
return preview.getForm() == PonyForm.SEAPONY;
return preview.forceSeapony();
}
return Pony.getManager().getPony(entity).filter(pony -> {
return (pony.race() == Race.SEAPONY
|| (entity instanceof PlayerEntity player && SkinsProxy.getInstance().getSkin(DefaultPonySkinHelper.SEAPONY_SKIN_TYPE_ID, player).isPresent())
|| (entity instanceof AbstractClientPlayerEntity player && SkinsProxy.instance.getSkin(DefaultPonySkinHelper.SEAPONY_SKIN_TYPE_ID, player).isPresent())
);
}).isPresent();
}
public static boolean isNirikModifier(LivingEntity entity) {
if (entity instanceof PreviewModel preview) {
return preview.forceNirik();
}
return false;
}
public static boolean hasNirikForm(LivingEntity entity) {
if (entity instanceof PreviewModel preview) {
return preview.getForm() == PonyForm.NIRIK;
return preview.forceNirik();
}
return Pony.getManager().getPony(entity).filter(pony -> {
return (pony.race() == Race.KIRIN
&& (entity instanceof PlayerEntity player && SkinsProxy.getInstance().getSkin(DefaultPonySkinHelper.NIRIK_SKIN_TYPE_ID, player).isPresent())
&& (entity instanceof AbstractClientPlayerEntity player && SkinsProxy.instance.getSkin(DefaultPonySkinHelper.NIRIK_SKIN_TYPE_ID, player).isPresent())
);
}).isPresent();
}

View file

@ -1,60 +1,28 @@
package com.minelittlepony.api.pony;
import java.util.*;
import java.util.concurrent.TimeUnit;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.google.common.cache.*;
import com.minelittlepony.server.MineLittlePonyServer;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTextures;
import java.util.Optional;
import java.util.Set;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.texture.PlayerSkinProvider;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
/**
* Proxy handler for getting player skin data from HDSkins
*/
public class SkinsProxy {
public static SkinsProxy INSTANCE = new SkinsProxy();
private static final SkinsProxy DEFAULT = INSTANCE;
public static SkinsProxy instance = new SkinsProxy();
private final LoadingCache<GameProfile, GameProfile> profileCache = CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.SECONDS)
.build(CacheLoader.from(profile -> {
var result = MineLittlePonyServer.getServer().getSessionService().fetchProfile(profile.getId(), false);
return result == null ? profile : result.profile();
}));
public static SkinsProxy getInstance() {
return INSTANCE;
}
protected SkinsProxy() {
if (INSTANCE == DEFAULT) {
INSTANCE = this;
}
}
@Nullable
public Identifier getSkinTexture(GameProfile profile) {
MinecraftServer server = MineLittlePonyServer.getServer();
if (server != null) {
profile = profileCache.getUnchecked(profile);
MinecraftProfileTextures textures = server.getSessionService().getTextures(profile);
if (textures != MinecraftProfileTextures.EMPTY) {
return Identifier.of(textures.skin().getUrl());
}
}
return null;
PlayerSkinProvider skins = MinecraftClient.getInstance().getSkinProvider();
return skins.getSkinTextures(profile).texture();
}
public Optional<Identifier> getSkin(Identifier skinTypeId, PlayerEntity player) {
public Optional<Identifier> getSkin(Identifier skinTypeId, AbstractClientPlayerEntity player) {
return Optional.empty();
}

View file

@ -2,12 +2,7 @@ package com.minelittlepony.api.pony.meta;
import net.minecraft.network.PacketByteBuf;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.*;
import java.util.stream.Collectors;
public record Flags<T extends Enum<T> & TValue<T>> (
T def,
@ -15,15 +10,6 @@ public record Flags<T extends Enum<T> & TValue<T>> (
int colorCode
) implements Comparable<Flags<T>>, TValue<T> {
public static <T extends Enum<T> & TValue<T>> Codec<Flags<T>> codec(T def, Codec<T> elementCodec) {
Codec<Set<T>> setCodec = Codec.list(elementCodec).xmap(elements -> elements.stream().distinct().collect(Collectors.toUnmodifiableSet()), set -> List.copyOf(set));
return Codec.xor(setCodec.xmap(elements -> new Flags<>(def, elements, 0), flags -> flags.values()), RecordCodecBuilder.<Flags<T>>create(i -> i.group(
elementCodec.fieldOf("def").forGetter(Flags::def),
setCodec.fieldOf("values").forGetter(Flags::values),
Codec.INT.fieldOf("colorCode").forGetter(Flags::colorCode)
).apply(i, Flags::new))).xmap(Either::unwrap, Either::left);
}
public static <T extends Enum<T> & TValue<T>> Flags<T> of(T def) {
return new Flags<>(def, Set.<T>of(), 0);
}
@ -41,7 +27,7 @@ public record Flags<T extends Enum<T> & TValue<T>> (
for (int i = 0; i < length; i++) {
values.add(all[buffer.readInt()]);
}
return of(def, buffer.readInt(), values);
return new Flags<>(def, values, buffer.readInt());
}
public void write(PacketByteBuf buffer) {

View file

@ -1,16 +1,10 @@
package com.minelittlepony.api.pony.meta;
import net.minecraft.util.StringIdentifiable;
import com.mojang.serialization.Codec;
public enum Gender implements TValue<Gender> {
MARE(0),
STALLION(0xffffff),
ABOMONATION(0x888888);
public static final Codec<Gender> CODEC = StringIdentifiable.createCodec(Gender::values);
private int triggerValue;
Gender(int pixel) {

View file

@ -1,41 +0,0 @@
package com.minelittlepony.api.pony.meta;
import net.minecraft.util.math.ColorHelper;
import org.jetbrains.annotations.Nullable;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
public interface Mats {
static TriggerPixel.Mat createMat(MinecraftProfileTexture texture) throws IOException {
return createMat(URI.create(texture.getUrl()).toURL());
}
static TriggerPixel.Mat createMat(URL url) throws IOException {
try {
@Nullable
BufferedImage image = ImageIO.read(url);
if (image == null) {
throw new IOException("Unable to read image from url " + url);
}
Raster raster = image.getData();
return (x, y) -> {
if (x < 0 || y < 0 || x > raster.getWidth() || y > raster.getHeight()) {
return 0;
}
int[] color = raster.getPixel(x, y, new int[] {0, 0, 0, 0});
return ColorHelper.getArgb(color[3], color[0], color[1], color[2]);
};
} catch (IllegalArgumentException e) {
throw new IOException("Could not create mat from image", e);
}
}
}

View file

@ -1,9 +1,5 @@
package com.minelittlepony.api.pony.meta;
import net.minecraft.util.StringIdentifiable;
import com.mojang.serialization.Codec;
public enum Race implements TValue<Race> {
HUMAN (0x000000, false, false),
EARTH (0xf9b131, false, false),
@ -19,8 +15,6 @@ public enum Race implements TValue<Race> {
BATPONY (0xeeeeee, true, false),
SEAPONY (0x3655dd, false, true);
public static final Codec<Race> CODEC = StringIdentifiable.createCodec(Race::values);
private final boolean wings;
private final boolean horn;

View file

@ -1,9 +1,6 @@
package com.minelittlepony.api.pony.meta;
import net.minecraft.util.StringIdentifiable;
import com.minelittlepony.api.config.PonyConfig;
import com.mojang.serialization.Codec;
/**
* Represents the different model sizes that are possible.
@ -17,14 +14,10 @@ public enum SizePreset implements Size {
BULKY (0xce3254, 0.5f, 1, 1.05F),
LANKY (0x3254ce, 0.45F, 0.85F, 0.9F),
NORMAL (0x000000, 0.4f, 0.8F, 0.8F),
STOCKY (0xb2e7dd, 0.45F, 0.8F, 0.8F),
SQUAT (0xa3d2c7, 0.4F, 0.7F, 0.67F),
YEARLING(0x53beff, 0.4F, 0.6F, 0.65F),
FOAL (0xffbe53, 0.25f, 0.6F, 0.5F),
UNSET (0x000000, 1, 1, 1);
public static final Codec<SizePreset> CODEC = StringIdentifiable.createCodec(SizePreset::values);
private final int triggerValue;
private final float shadowSize;
private final float scale;

View file

@ -1,14 +1,12 @@
package com.minelittlepony.api.pony.meta;
import net.minecraft.util.StringIdentifiable;
import java.util.Arrays;
import java.util.List;
/**
* Interface for enums that can be parsed from an image trigger pixel value.
*/
public interface TValue<T> extends StringIdentifiable {
public interface TValue<T> {
/**
* Gets the pixel colour matching this enum value.
*/
@ -26,11 +24,6 @@ public interface TValue<T> extends StringIdentifiable {
*/
String name();
@Override
default String asString() {
return name();
}
default String getHexValue() {
return toHex(colorCode());
}

View file

@ -1,9 +1,5 @@
package com.minelittlepony.api.pony.meta;
import net.minecraft.util.StringIdentifiable;
import com.mojang.serialization.Codec;
public enum TailLength implements TValue<TailLength> {
STUB (0x425844),
QUARTER (0xd19fe4),
@ -11,8 +7,6 @@ public enum TailLength implements TValue<TailLength> {
THREE_QUARTERS (0x8a6b7f),
FULL (0x000000);
public static final Codec<TailLength> CODEC = StringIdentifiable.createCodec(TailLength::values);
private int triggerValue;
TailLength(int pixel) {

View file

@ -1,17 +1,11 @@
package com.minelittlepony.api.pony.meta;
import net.minecraft.util.StringIdentifiable;
import com.mojang.serialization.Codec;
public enum TailShape implements TValue<TailShape> {
STRAIGHT(0),
BUMPY (0xfc539f),
SWIRLY (0x3eff22),
SPIKY (0x3308c7);
public static final Codec<TailShape> CODEC = StringIdentifiable.createCodec(TailShape::values);
private int triggerValue;
TailShape(int pixel) {

View file

@ -5,6 +5,8 @@ import net.minecraft.util.math.ColorHelper;
import org.joml.Vector2i;
import com.minelittlepony.common.util.Color;
import java.util.*;
/**
@ -27,9 +29,9 @@ public interface TriggerPixel<T> {
MAX_COORDS.y = Math.max(MAX_COORDS.y, y);
Int2ObjectOpenHashMap<T> lookup = buildLookup(options);
return image -> {
int color = image.getColor(x, y);
int color = Color.abgrToArgb(image.getColor(x, y));
if (ColorHelper.getAlpha(color) < 255) {
if (ColorHelper.Argb.getAlpha(color) < 255) {
return (T)def;
}
return lookup.getOrDefault(color & 0x00FFFFFF, def);
@ -39,7 +41,7 @@ public interface TriggerPixel<T> {
static TriggerPixel<Integer> ofColor(int x, int y) {
MAX_COORDS.x = Math.max(MAX_COORDS.x, x);
MAX_COORDS.y = Math.max(MAX_COORDS.y, y);
return image -> image.getColor(x, y);
return image -> Color.abgrToArgb(image.getColor(x, y));
}
static <T extends Enum<T> & TValue<T>> TriggerPixel<Flags<T>> ofFlags(int x, int y, Flags<T> def, T[] options) {
@ -53,15 +55,15 @@ public interface TriggerPixel<T> {
}
};
return image -> {
int color = image.getColor(x, y);
if (ColorHelper.getAlpha(color) < 255) {
int color = Color.abgrToArgb(image.getColor(x, y));
if (ColorHelper.Argb.getAlpha(color) < 255) {
return def;
}
@SuppressWarnings("unchecked")
Set<T> values = EnumSet.noneOf((Class<T>)def.def().getClass());
if (flagReader.readFlag(ColorHelper.getRed(color), values)
| flagReader.readFlag(ColorHelper.getGreen(color), values)
| flagReader.readFlag(ColorHelper.getBlue(color), values)) {
if (flagReader.readFlag(ColorHelper.Argb.getRed(color), values)
| flagReader.readFlag(ColorHelper.Argb.getGreen(color), values)
| flagReader.readFlag(ColorHelper.Argb.getBlue(color), values)) {
return new Flags<>(def.def(), values, color & 0x00FFFFFF);
}
return def;

View file

@ -1,26 +1,22 @@
package com.minelittlepony.api.pony.meta;
import net.minecraft.util.Identifier;
import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.math.ColorHelper;
import com.minelittlepony.api.pony.Pony;
import com.mojang.serialization.Codec;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public enum Wearable implements TValue<Wearable> {
NONE (0x00, null),
CROWN (0x16, Pony.id("textures/models/crown.png")),
MUFFIN (0x32, Pony.id("textures/models/muffin.png")),
HAT (0x64, Identifier.ofVanilla("textures/entity/witch.png")),
ANTLERS (0x96, Pony.id("textures/models/antlers.png")),
SADDLE_BAGS_LEFT (0xC6, Pony.id("textures/models/saddlebags.png")),
SADDLE_BAGS_RIGHT (0xC7, Pony.id("textures/models/saddlebags.png")),
SADDLE_BAGS_BOTH (0xC8, Pony.id("textures/models/saddlebags.png")),
STETSON (0xFA, Pony.id("textures/models/stetson.png"));
CROWN (0x16, new Identifier("minelittlepony", "textures/models/crown.png")),
MUFFIN (0x32, new Identifier("minelittlepony", "textures/models/muffin.png")),
HAT (0x64, new Identifier("textures/entity/witch.png")),
ANTLERS (0x96, new Identifier("minelittlepony", "textures/models/antlers.png")),
SADDLE_BAGS_LEFT (0xC6, new Identifier("minelittlepony", "textures/models/saddlebags.png")),
SADDLE_BAGS_RIGHT (0xC7, new Identifier("minelittlepony", "textures/models/saddlebags.png")),
SADDLE_BAGS_BOTH (0xC8, new Identifier("minelittlepony", "textures/models/saddlebags.png")),
STETSON (0xFA, new Identifier("minelittlepony", "textures/models/stetson.png"));
private int triggerValue;
@ -31,12 +27,9 @@ public enum Wearable implements TValue<Wearable> {
public static final Flags<Wearable> EMPTY_FLAGS = Flags.of(NONE);
public static final Codec<Wearable> CODEC = StringIdentifiable.createCodec(Wearable::values);
public static final Codec<Flags<Wearable>> FLAGS_CODEC = Flags.codec(NONE, CODEC);
Wearable(int pixel, Identifier texture) {
triggerValue = pixel;
id = Pony.id(name().toLowerCase(Locale.ROOT));
id = new Identifier("minelittlepony", name().toLowerCase(Locale.ROOT));
this.texture = texture;
}
@ -59,6 +52,6 @@ public enum Wearable implements TValue<Wearable> {
@Override
public int getChannelAdjustedColorCode() {
return triggerValue == 0 ? 0 : ColorHelper.getArgb(255, triggerValue, triggerValue, triggerValue);
return triggerValue == 0 ? 0 : ColorHelper.Argb.getArgb(255, triggerValue, triggerValue, triggerValue);
}
}

View file

@ -1,16 +0,0 @@
package com.minelittlepony.client;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.pony.SkinsProxy;
import com.mojang.authlib.GameProfile;
public class ClientSkinsProxy extends SkinsProxy {
@Nullable
public Identifier getSkinTexture(GameProfile profile) {
return MinecraftClient.getInstance().getSkinProvider().getSkinTextures(profile).texture();
}
}

View file

@ -2,7 +2,6 @@ package com.minelittlepony.client;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerPosition;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.*;
@ -21,11 +20,11 @@ public class HorseCam {
* Restores the previous camera (unadjusted) angle for the client when the server sends an update.
* This is to prevent issues caused by the server updating our pitch whenever the player leaves a portal.
*/
public static PlayerPosition transformIncomingServerCameraAngle(PlayerPosition change) {
if (MathHelper.approximatelyEquals(change.pitch(), lastComputedPitch)) {
return new PlayerPosition(change.position(), change.deltaMovement(), change.yaw(), lastOriginalPitch);
public static float transformIncomingServerCameraAngle(float serverPitch) {
if (MathHelper.approximatelyEquals(serverPitch, lastComputedPitch)) {
return lastOriginalPitch;
}
return change;
return serverPitch;
}
/**
@ -88,7 +87,7 @@ public class HorseCam {
public static float rescaleCameraPitch(double toHeight, float originalPitch) {
MinecraftClient client = MinecraftClient.getInstance();
PlayerEntity player = client.player;
client.gameRenderer.updateCrosshairTarget(client.getRenderTickCounter().getTickDelta(false));
client.gameRenderer.updateTargetedEntity(client.getTickDelta());
HitResult hit = client.crosshairTarget;
if (client.targetedEntity != null) {

View file

@ -14,8 +14,8 @@ import static com.minelittlepony.common.event.SkinFilterCallback.copy;
*/
class LegacySkinConverter implements SkinFilterCallback {
@Override
public NativeImage processImage(NativeImage image, int initialWidth, int initialHeight) {
if (SkinFilterCallback.isLegacyAspectRatio(initialWidth, initialHeight)) {
public void processImage(NativeImage image, boolean legacy) {
if (legacy) {
// ( from ) ( offset ) (size) flipX flipY
copy(image, 58, 16, 0, 16, 2, 2, true, false); // top
copy(image, 60, 16, 0, 16, 2, 2, true, false); // bottom
@ -24,7 +24,5 @@ class LegacySkinConverter implements SkinFilterCallback {
copy(image, 56, 18, 4, 16, 2, 14, true, false); // outside
copy(image, 62, 18, 0, 16, 2, 14, true, false); // front
}
return image;
}
}

View file

@ -1,7 +1,7 @@
package com.minelittlepony.client;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.events.ClientChannel;
import com.minelittlepony.api.events.Channel;
import com.minelittlepony.client.model.ModelType;
import com.minelittlepony.client.model.armour.ArmourTextureResolver;
import com.minelittlepony.client.render.MobRenderers;
@ -9,12 +9,12 @@ import com.minelittlepony.client.render.PonyRenderDispatcher;
import com.minelittlepony.common.client.gui.VisibilityMode;
import com.minelittlepony.common.client.gui.element.Button;
import com.minelittlepony.common.client.gui.sprite.TextureSprite;
import com.minelittlepony.common.event.ClientReadyCallback;
import com.minelittlepony.common.event.ScreenInitCallback;
import com.minelittlepony.common.event.SkinFilterCallback;
import com.minelittlepony.common.util.GamePaths;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicBoolean;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
@ -40,7 +40,7 @@ public class MineLittlePony implements ClientModInitializer {
private static MineLittlePony instance;
public static final Logger LOGGER = LogManager.getLogger("MineLittlePony");
public static final Logger logger = LogManager.getLogger("MineLittlePony");
private PonyManagerImpl ponyManager;
private VariatedTextureSupplier variatedTextures;
@ -48,8 +48,6 @@ public class MineLittlePony implements ClientModInitializer {
private final KeyBinding keyBinding = new KeyBinding("key.minelittlepony.settings", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_F9, "key.categories.misc");
private final PonyRenderDispatcher renderDispatcher = new PonyRenderDispatcher();
private final AtomicBoolean initialized = new AtomicBoolean();
private final AtomicBoolean configChanged = new AtomicBoolean();
private boolean hasHdSkins;
private boolean hasModMenu;
@ -65,10 +63,6 @@ public class MineLittlePony implements ClientModInitializer {
return instance;
}
public static Identifier id(String name) {
return Identifier.of("minelittlepony", name);
}
@Override
public void onInitializeClient() {
hasHdSkins = FabricLoader.getInstance().isModLoaded("hdskins");
@ -82,34 +76,27 @@ public class MineLittlePony implements ClientModInitializer {
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(ponyManager);
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(variatedTextures);
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(ArmourTextureResolver.INSTANCE);
// convert legacy pony skins
SkinFilterCallback.EVENT.register(new LegacySkinConverter());
// general events
ClientReadyCallback.Handler.register();
ClientTickEvents.END_CLIENT_TICK.register(this::onTick);
ClientReadyCallback.EVENT.register(client -> {
renderDispatcher.initialise(client.getEntityRenderDispatcher(), false);
});
ScreenInitCallback.EVENT.register(this::onScreenInit);
new ClientSkinsProxy();
config.load();
config.onChangedExternally(c -> configChanged.set(true));
ClientChannel.bootstrap();
Channel.bootstrap();
ModelType.bootstrap();
FabricLoader.getInstance().getEntrypoints("minelittlepony", ClientModInitializer.class).forEach(ClientModInitializer::onInitializeClient);
}
private void onTick(MinecraftClient client) {
if (!initialized.getAndSet(true)) {
renderDispatcher.initialise(client.getEntityRenderDispatcher(), false);
}
if (configChanged.getAndSet(false) && client.currentScreen instanceof PonySettingsScreen screen) {
screen.init(client, screen.width, screen.height);
}
boolean inGame = client.world != null && client.player != null && client.currentScreen == null;
boolean mainMenu = client.currentScreen instanceof TitleScreen;
@ -137,7 +124,7 @@ public class MineLittlePony implements ClientModInitializer {
button.getStyle()
.setIcon(new TextureSprite()
.setPosition(2, 2)
.setTexture(id("textures/gui/pony.png"))
.setTexture(new Identifier("minelittlepony", "textures/gui/pony.png"))
.setTextureSize(16, 16)
.setSize(16, 16))
.setTooltip("minelp.options.title", 0, 10);
@ -164,7 +151,7 @@ public class MineLittlePony implements ClientModInitializer {
private static final class ClientPonyConfig extends PonyConfig {
public ClientPonyConfig(Path path) {
super(path);
MobRenderers.REGISTRY.values().forEach(r -> value("entities", r.name(), true));
MobRenderers.REGISTRY.values().forEach(r -> value("entities", r.name, true));
disablePonifiedArmour.onChanged(t -> ArmourTextureResolver.INSTANCE.invalidate());
}

View file

@ -0,0 +1,50 @@
package com.minelittlepony.client;
import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.api.pony.PonyPosture;
import com.minelittlepony.client.transform.PonyTransformation;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class PonyBounds {
public static Vec3d getAbsoluteRidingOffset(LivingEntity entity) {
return PonyPosture.getMountPony(entity).map(ridingPony -> {
LivingEntity vehicle = (LivingEntity)entity.getVehicle();
Vec3d offset = PonyTransformation.forSize(ridingPony.size()).getRiderOffset();
float scale = ridingPony.metadata().size().scaleFactor();
return getAbsoluteRidingOffset(vehicle).add(
0,
offset.y - vehicle.getHeight() * 1 / scale,
0
);
}).orElseGet(() -> getBaseRidingOffset(entity));
}
private static Vec3d getBaseRidingOffset(LivingEntity entity) {
float delta = MinecraftClient.getInstance().getTickDelta();
Entity vehicle = entity.getVehicle();
double vehicleOffset = vehicle == null ? 0 : vehicle.getHeight();
return new Vec3d(
MathHelper.lerp(delta, entity.prevX, entity.getX()),
MathHelper.lerp(delta, entity.prevY, entity.getY()) + vehicleOffset,
MathHelper.lerp(delta, entity.prevZ, entity.getZ())
);
}
public static Box getBoundingBox(Pony pony, LivingEntity entity) {
final float scale = pony.size().scaleFactor() + 0.1F;
final float width = entity.getWidth() * scale;
final float height = entity.getHeight() * scale;
return new Box(-width, height, -width, width, 0, width).offset(getAbsoluteRidingOffset(entity));
}
}

View file

@ -1,9 +1,10 @@
package com.minelittlepony.client;
import net.minecraft.client.MinecraftClient;
import net.minecraft.resource.metadata.ResourceMetadataSerializer;
import net.minecraft.resource.metadata.ResourceMetadataReader;
import net.minecraft.util.Identifier;
import com.google.gson.*;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.client.util.render.NativeUtil;
@ -14,9 +15,23 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
public class PonyDataLoader {
public static final Supplier<Optional<PonyData>> NULL = loaded(PonyData.NULL);
private static final ResourceMetadataSerializer<PonyData> SERIALIZER = new ResourceMetadataSerializer<PonyData>("pony", PonyData.CODEC);
class PonyDataLoader {
static final Supplier<Optional<PonyData>> NULL = loaded(PonyData.NULL);
private static final ResourceMetadataReader<PonyData> SERIALIZER = new ResourceMetadataReader<PonyData>() {
private static final Gson GSON = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
@Override
public String getKey() {
return "pony";
}
@Override
public PonyData fromJson(JsonObject json) {
return GSON.fromJson(json, PonyData.class);
}
};
/**
* Parses the given resource into a new IPonyData.
@ -31,7 +46,7 @@ public class PonyDataLoader {
try {
return res.getMetadata().decode(SERIALIZER);
} catch (IOException e) {
MineLittlePony.LOGGER.warn("Unable to read {} metadata", identifier, e);
MineLittlePony.logger.warn("Unable to read {} metadata", identifier, e);
}
return Optional.empty();
}).map(PonyDataLoader::loaded).orElseGet(() -> {
@ -39,7 +54,7 @@ public class PonyDataLoader {
NativeUtil.parseImage(identifier, image -> {
callback.accept(new PonyData(image, noSkin));
}, e -> {
MineLittlePony.LOGGER.fatal("Unable to read {} metadata", identifier, e);
MineLittlePony.logger.fatal("Unable to read {} metadata", identifier, e);
callback.accept(PonyData.NULL);
});
});

View file

@ -1,41 +1,39 @@
package com.minelittlepony.client;
import com.google.common.base.MoreObjects;
import com.google.common.cache.*;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.config.PonyLevel;
import com.minelittlepony.api.events.PonySkinResolver;
import com.minelittlepony.api.pony.*;
import com.minelittlepony.client.render.blockentity.skull.PonySkullRenderer;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import net.minecraft.client.util.DefaultSkinHelper;
import net.minecraft.resource.ResourceManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
import java.util.*;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class PonyManagerImpl implements PonyManager, SimpleSynchronousResourceReloadListener {
private static final Identifier ID = MineLittlePony.id("background_ponies");
private static final Identifier ID = new Identifier("minelittlepony", "background_ponies");
private final PonyConfig config;
private final LoadingCache<Key, Pony> poniesCache = CacheBuilder.newBuilder()
private final LoadingCache<Identifier, Pony> defaultedPoniesCache = CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.SECONDS)
.build(CacheLoader.from(key -> new Pony(key.texture(), PonyDataLoader.parse(key.texture(), key.defaulted()))));
private final WeakHashMap<UUID, Pony> playerPonies = new WeakHashMap<>();
.build(CacheLoader.from(resource -> new Pony(resource, PonyDataLoader.parse(resource, true))));
record Key(Identifier texture, boolean defaulted) {}
private final LoadingCache<Identifier, Pony> poniesCache = CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.SECONDS)
.build(CacheLoader.from(resource -> new Pony(resource, PonyDataLoader.parse(resource, false))));
public PonyManagerImpl(PonyConfig config) {
this.config = config;
@ -44,7 +42,7 @@ public class PonyManagerImpl implements PonyManager, SimpleSynchronousResourceRe
private Pony loadPony(Identifier resource, boolean defaulted) {
try {
return poniesCache.get(new Key(resource, defaulted));
return (defaulted ? defaultedPoniesCache : poniesCache).get(resource);
} catch (ExecutionException e) {
return new Pony(resource, PonyDataLoader.NULL);
}
@ -52,26 +50,7 @@ public class PonyManagerImpl implements PonyManager, SimpleSynchronousResourceRe
@Override
public Pony getPony(PlayerEntity player) {
final UUID id = player instanceof ForcedPony ? null : player.getGameProfile() == null || player.getGameProfile().getId() == null ? player.getUuid() : player.getGameProfile().getId();
Pony pony;
if (player instanceof ServerPlayerEntity && id != null) {
pony = playerPonies.get(id);
if (pony != null) {
return pony;
}
}
@Nullable
Identifier skin = getSkin(player);
if (skin != null) {
skin = MoreObjects.firstNonNull(PonySkinResolver.EVENT.invoker().onPonySkinResolving(player, s -> getPony(s, id), skin), skin);
}
pony = getPony(skin, id);
if (!(player instanceof ServerPlayerEntity) && id != null) {
playerPonies.put(id, pony);
}
return pony;
return getPony(getSkin(player), player instanceof ForcedPony ? null : player.getGameProfile() == null ? player.getUuid() : player.getGameProfile().getId());
}
@Override
@ -79,11 +58,7 @@ public class PonyManagerImpl implements PonyManager, SimpleSynchronousResourceRe
if (entity instanceof PlayerEntity player) {
return Optional.of(getPony(player));
}
@Nullable
Identifier skin = getSkin(entity);
if (skin != null) {
skin = MoreObjects.firstNonNull(PonySkinResolver.EVENT.invoker().onPonySkinResolving(entity, s -> getPony(s, null), skin), skin);
}
return skin == null ? Optional.empty() : Optional.of(getPony(skin, null));
}
@ -109,7 +84,6 @@ public class PonyManagerImpl implements PonyManager, SimpleSynchronousResourceRe
return loadPony(DefaultSkinHelper.getSkinTextures(uuid).texture(), true);
}
@SuppressWarnings("unchecked")
@Nullable
private Identifier getSkin(LivingEntity entity) {
if (entity instanceof PlayerEntity player) {
@ -117,8 +91,8 @@ public class PonyManagerImpl implements PonyManager, SimpleSynchronousResourceRe
return clientPlayer.getSkinTextures().texture();
}
} else {
if (MineLittlePony.getInstance().getRenderDispatcher().getPonyRenderer(entity) instanceof LivingEntityRenderer renderer) {
return renderer.getTexture((LivingEntityRenderState)renderer.getAndUpdateRenderState(entity, 1));
if (MineLittlePony.getInstance().getRenderDispatcher().getPonyRenderer(entity) != null) {
return MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity).getTexture(entity);
}
}
@ -127,17 +101,19 @@ public class PonyManagerImpl implements PonyManager, SimpleSynchronousResourceRe
public void removePony(Identifier resource) {
poniesCache.invalidate(resource);
defaultedPoniesCache.invalidate(resource);
}
public void clearCache() {
MineLittlePony.LOGGER.info("Turned {} cached ponies into cupcakes.", poniesCache.size());
MineLittlePony.logger.info("Flushed {} cached ponies.", poniesCache.size());
poniesCache.invalidateAll();
defaultedPoniesCache.invalidateAll();
}
@Override
public void reload(ResourceManager manager) {
public void reload(ResourceManager var1) {
clearCache();
PonySkullRenderer.INSTANCE.reload();
PonySkullRenderer.reload();
}
@Override

View file

@ -136,11 +136,10 @@ public class PonySettingsScreen extends GameGui {
if (hiddenOptions) {
for (Setting<?> i : config.getCategory("customisation").entries()) {
if (i.get() instanceof Boolean value) {
content.addButton(new Toggle(LEFT, row += 20, value))
.onChange((Setting<Boolean>)i)
.getStyle().setText(OPTIONS_PREFIX + i.name().toLowerCase());
}
Button button = content
.addButton(new Toggle(LEFT, row += 20, ((Setting<Boolean>)i).get()))
.onChange((Setting<Boolean>)i);
button.getStyle().setText(OPTIONS_PREFIX + i.name().toLowerCase());
}
}
@ -157,9 +156,9 @@ public class PonySettingsScreen extends GameGui {
content.addButton(new Label(RIGHT, row)).getStyle().setText(MOB_PREFIX + "title");
for (MobRenderers i : MobRenderers.REGISTRY.values()) {
content.addButton(new Toggle(RIGHT, row += 20, i.option()))
.onChange(i.option())
.getStyle().setText(MOB_PREFIX + i.name());
content.addButton(new Toggle(RIGHT, row += 20, i.get()))
.onChange(i::set)
.getStyle().setText(MOB_PREFIX + i.name);
}
row += 15;

View file

@ -11,9 +11,9 @@ import com.minelittlepony.util.MathUtil;
import java.util.*;
public class VariatedTextureSupplier implements SimpleSynchronousResourceReloadListener {
private static final Identifier ID = MineLittlePony.id("variated_textures");
public static final Identifier BACKGROUND_PONIES_POOL = MineLittlePony.id("textures/entity/pony");
public static final Identifier BACKGROUND_ZOMPONIES_POOL = MineLittlePony.id("textures/entity/zompony");
private static final Identifier ID = new Identifier("minelittlepony", "variated_textures");
public static final Identifier BACKGROUND_PONIES_POOL = new Identifier("minelittlepony", "textures/entity/pony");
public static final Identifier BACKGROUND_ZOMPONIES_POOL = new Identifier("minelittlepony", "textures/entity/zompony");
private final Map<Identifier, SkinList> entries = new HashMap<>();
@ -75,7 +75,7 @@ public class VariatedTextureSupplier implements SimpleSynchronousResourceReloadL
public void reloadAll(ResourceManager resourceManager) {
textures.clear();
textures.addAll(resourceManager.findResources(id.getPath(), path -> path.getPath().endsWith(".png")).keySet());
MineLittlePony.LOGGER.info("Detected {} ponies installed at {}.", textures.size(), id);
MineLittlePony.logger.info("Detected {} ponies installed at {}.", textures.size(), id);
}
static boolean isUser(UUID uuid) {

View file

@ -1,7 +1,6 @@
package com.minelittlepony.client.compat.hdskins;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.Identifier;
import com.minelittlepony.api.model.PreviewModel;
import com.minelittlepony.api.pony.*;
@ -21,13 +20,12 @@ class DummyPony extends DummyPlayer implements PreviewModel, PonyManager.ForcedP
}
@Override
public Identifier getForm() {
if (getTextures().getPosture().getActiveSkinType() == MineLPHDSkins.seaponySkinType) {
return PonyForm.SEAPONY;
}
if (getTextures().getPosture().getActiveSkinType() == MineLPHDSkins.nirikSkinType) {
return PonyForm.NIRIK;
}
return PonyForm.DEFAULT;
public boolean forceSeapony() {
return getTextures().getPosture().getActiveSkinType() == MineLPHDSkins.seaponySkinType;
}
@Override
public boolean forceNirik() {
return getTextures().getPosture().getActiveSkinType() == MineLPHDSkins.nirikSkinType;
}
}

View file

@ -17,22 +17,22 @@ import net.minecraft.util.Identifier;
* Skin uploading GUI. Usually displayed over the main menu.
*/
class GuiSkinsMineLP extends GuiSkins {
private static final Identifier[] PANORAMAS = new Identifier[] {
MineLittlePony.id("textures/cubemap/sugarcubecorner"),
MineLittlePony.id("textures/cubemap/quillsandsofas"),
MineLittlePony.id("textures/cubemap/sweetappleacres")
private static final String[] PANORAMAS = new String[] {
"minelittlepony:textures/cubemap/sugarcubecorner",
"minelittlepony:textures/cubemap/quillsandsofas",
"minelittlepony:textures/cubemap/sweetappleacres"
};
public GuiSkinsMineLP(Screen parent, SkinServerList servers) {
super(parent, servers);
chooser.addSkinChangedEventListener(type -> {
MineLittlePony.LOGGER.debug("Invalidating old local skin, checking updated local skin");
MineLittlePony.logger.debug("Invalidating old local skin, checking updated local skin");
if (type == SkinType.SKIN) {
MineLittlePony.getInstance().getManager().removePony(previewer.getLocal().getSkins().get(SkinType.SKIN).getId());
}
});
uploader.addSkinLoadedEventListener((type, location, profileTexture) -> {
MineLittlePony.LOGGER.debug("Invalidating old remote skin, checking updated remote skin");
MineLittlePony.logger.debug("Invalidating old remote skin, checking updated remote skin");
if (type == SkinType.SKIN) {
MineLittlePony.getInstance().getManager().removePony(location);
}
@ -47,7 +47,7 @@ class GuiSkinsMineLP extends GuiSkins {
.getStyle()
.setIcon(new TextureSprite()
.setPosition(2, 2)
.setTexture(MineLittlePony.id("textures/gui/pony.png"))
.setTexture(new Identifier("minelittlepony", "textures/gui/pony.png"))
.setTextureSize(16, 16)
.setSize(16, 16))
.setTooltip("minelp.options.title", 0, 10);
@ -64,6 +64,8 @@ class GuiSkinsMineLP extends GuiSkins {
@Override
protected Identifier getBackground() {
return PANORAMAS[(int)Math.floor(Math.random() * PANORAMAS.length)];
int i = (int)Math.floor(Math.random() * PANORAMAS.length);
return new Identifier(PANORAMAS[i]);
}
}

View file

@ -6,13 +6,14 @@ import com.minelittlepony.api.pony.*;
import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.common.client.gui.ScrollContainer;
import com.minelittlepony.common.client.gui.element.Button;
import com.minelittlepony.hdskins.HDSkinsServer;
import com.minelittlepony.common.event.ClientReadyCallback;
import com.minelittlepony.hdskins.client.*;
import com.minelittlepony.hdskins.client.gui.GuiSkins;
import com.minelittlepony.hdskins.client.gui.player.DummyPlayer;
import com.minelittlepony.hdskins.client.gui.player.skins.PlayerSkins.PlayerSkin;
import com.minelittlepony.hdskins.client.profile.SkinLoader.ProvidedSkins;
import com.minelittlepony.hdskins.profile.SkinType;
import com.mojang.authlib.GameProfile;
import java.util.*;
@ -24,7 +25,6 @@ import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
@ -33,52 +33,45 @@ import com.minelittlepony.client.*;
/**
* All the interactions with HD Skins.
*/
public class MineLPHDSkins extends ClientSkinsProxy implements ClientModInitializer {
public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer {
static SkinType seaponySkinType;
static SkinType nirikSkinType;
static final Map<SkinType, Wearable> WEARABLE_TYPES = new HashMap<>();
static final Map<SkinType, Wearable> wearableTypes = new HashMap<>();
@Override
public void onInitializeClient() {
SkinsProxy.instance = this;
PonySettingsScreen.buttonFactory = this::renderOption;
seaponySkinType = SkinType.register(DefaultPonySkinHelper.SEAPONY_SKIN_TYPE_ID, Items.COD_BUCKET.getDefaultStack());
nirikSkinType = SkinType.register(DefaultPonySkinHelper.NIRIK_SKIN_TYPE_ID, Items.LAVA_BUCKET.getDefaultStack());
Wearable.REGISTRY.values().forEach(wearable -> {
if (wearable != Wearable.NONE) {
WEARABLE_TYPES.put(SkinType.register(wearable.getId(), Items.BUNDLE.getDefaultStack()), wearable);
wearableTypes.put(SkinType.register(wearable.getId(), Items.BUNDLE.getDefaultStack()), wearable);
}
});
// Clear ponies when skins are cleared
SkinCacheClearCallback.EVENT.register(() -> {
MineLittlePony.getInstance().getManager().clearCache();
ClientReadyCallback.EVENT.register(client -> {
// Clear ponies when skins are cleared
SkinCacheClearCallback.EVENT.register(MineLittlePony.getInstance().getManager()::clearCache);
// Ponify the skins GUI.
GuiSkins.setSkinsGui(GuiSkinsMineLP::new);
});
// Ponify the skins GUI.
GuiSkins.setSkinsGui(GuiSkinsMineLP::new);
HDSkins.getInstance().getSkinPrioritySorter().addSelector((skinType, playerSkins) -> {
if (skinType == SkinType.SKIN) {
if (skinType == SkinType.SKIN && PonyConfig.getInstance().mixedHumanSkins.get()) {
Optional<Pony> hdPony = getPony(playerSkins.hd());
Optional<Pony> vanillaPony = getPony(playerSkins.vanilla());
if (hdPony.isPresent() && vanillaPony.isPresent()) {
PonyLevel level = PonyConfig.getInstance().ponyLevel.get();
boolean vanillaHuman = vanillaPony.get().metadata().race().isHuman();
boolean hdHuman = hdPony.get().metadata().race().isHuman();
if (vanillaHuman != hdHuman) {
return (level == PonyLevel.HUMANS ? vanillaHuman : hdHuman) ? playerSkins.vanilla() : playerSkins.hd();
}
if (vanillaPony.get().metadata().priority() > hdPony.get().metadata().priority()) {
return playerSkins.vanilla();
}
if (hdPony.isPresent() && vanillaPony.isPresent()
&& vanillaPony.get().metadata().priority() > hdPony.get().metadata().priority()
&& (PonyConfig.getInstance().ponyLevel.get() == PonyLevel.HUMANS || vanillaPony.get().metadata().race().isHuman() == hdPony.get().metadata().race().isHuman())) {
return playerSkins.vanilla();
}
}
return playerSkins.combined();
});
}
@ -92,20 +85,15 @@ public class MineLPHDSkins extends ClientSkinsProxy implements ClientModInitiali
private void renderOption(Screen screen, @Nullable Screen parent, int row, int RIGHT, ScrollContainer content) {
content.addButton(new Button(RIGHT, row += 20, 150, 20))
.onClick(button -> MinecraftClient.getInstance().setScreen(
parent instanceof GuiSkins ? parent : GuiSkins.create(screen, HDSkinsServer.getInstance().getServers())
parent instanceof GuiSkins ? parent : GuiSkins.create(screen, HDSkins.getInstance().getSkinServerList())
))
.getStyle()
.setText("minelp.options.skins.hdskins.open");
}
@Override
public Optional<Identifier> getSkin(Identifier skinTypeId, PlayerEntity player) {
if (player instanceof AbstractClientPlayerEntity clientPlayer) {
return SkinType.REGISTRY.getOptionalValue(skinTypeId).flatMap(type -> getSkin(type, clientPlayer));
}
return Optional.empty();
public Optional<Identifier> getSkin(Identifier skinTypeId, AbstractClientPlayerEntity player) {
return SkinType.REGISTRY.getOrEmpty(skinTypeId).flatMap(type -> getSkin(type, player));
}
public Set<Identifier> getAvailableSkins(Entity entity) {
@ -146,17 +134,16 @@ public class MineLPHDSkins extends ClientSkinsProxy implements ClientModInitiali
}
}
return Optional.of(player)
.flatMap(PlayerSkins::of)
return Optional.of(player).flatMap(PlayerSkins::of)
.map(PlayerSkins::layers)
.map(PlayerSkinLayers::combined)
.flatMap(skins -> skins.getSkin(type));
.map(PlayerSkinLayers::combined).flatMap(skins -> skins.getSkin(type));
}
@Override
public Identifier getSkinTexture(GameProfile profile) {
return HDSkins.getInstance().getProfileRepository()
.load(profile).getNow(ProvidedSkins.EMPTY)
.load(profile)
.getNow(ProvidedSkins.EMPTY)
.getSkin(SkinType.SKIN)
.orElseGet(() -> super.getSkinTexture(profile));
}

View file

@ -35,7 +35,7 @@ class PonifiedDualCarouselWidget extends DualCarouselWidget {
return super.getDefaultSkin(SkinType.SKIN, modelVariant);
}
Wearable wearable = MineLPHDSkins.WEARABLE_TYPES.getOrDefault(type, Wearable.NONE);
Wearable wearable = MineLPHDSkins.wearableTypes.getOrDefault(type, Wearable.NONE);
if (wearable != Wearable.NONE) {
return NativeImageFilters.GREYSCALE.load(wearable.getDefaultTexture(), wearable.getDefaultTexture(), getExclusion());

View file

@ -0,0 +1,12 @@
package com.minelittlepony.client.mixin;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Entity.class)
public interface IResizeable {
@Accessor
void setStandingEyeHeight(float height);
}

View file

@ -2,8 +2,9 @@ package com.minelittlepony.client.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.minelittlepony.api.pony.Pony;
import net.minecraft.client.MinecraftClient;
@ -11,15 +12,18 @@ import net.minecraft.client.render.Camera;
@Mixin(Camera.class)
abstract class MixinCamera {
@ModifyReturnValue(method = "clipToSpace(F)F", at = @At("RETURN"))
private float redirectCameraDistance(float value) {
if (MinecraftClient.getInstance().player != null) {
Pony pony = Pony.getManager().getPony(MinecraftClient.getInstance().player);
@Inject(method = "clipToSpace(D)D",
at = @At("RETURN"),
cancellable = true)
private void redirectCameraDistance(double initial, CallbackInfoReturnable<Double> info) {
double value = info.getReturnValueD();
if (!pony.race().isHuman()) {
value *= pony.size().eyeDistanceFactor();
}
Pony pony = Pony.getManager().getPony(MinecraftClient.getInstance().player);
if (!pony.race().isHuman()) {
value *= pony.size().eyeDistanceFactor();
}
return value;
info.setReturnValue(value);
}
}

View file

@ -0,0 +1,59 @@
package com.minelittlepony.client.mixin;
import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.client.render.EquineRenderManager;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityPose;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ClientPlayerEntity.class)
abstract class MixinClientPlayerEntity extends AbstractClientPlayerEntity implements EquineRenderManager.RegistrationHandler {
public MixinClientPlayerEntity() { super(null, null); }
private final EquineRenderManager.SyncedPony syncedPony = new EquineRenderManager.SyncedPony();
@Inject(method = "startRiding(Lnet/minecraft/entity/Entity;Z)Z", at = @At("RETURN"))
private void onStartRiding(Entity entity, boolean bl, CallbackInfoReturnable<Boolean> info) {
calculateDimensions();
}
@Inject(method = "dismountVehicle()V", at = @At("RETURN"))
private void onStopRiding(CallbackInfo info) {
calculateDimensions();
}
@Override
public EquineRenderManager.SyncedPony getSyncedPony() {
return syncedPony;
}
@Override
public float getActiveEyeHeight(EntityPose pose, EntityDimensions dimensions) {
float value = super.getActiveEyeHeight(pose, dimensions);
Pony pony = Pony.getManager().getPony(this);
if (!pony.race().isHuman()) {
float factor = pony.size().eyeHeightFactor();
if (factor != 1) {
value *= factor;
if (hasVehicle()) {
value += getVehicle().getHeight();
}
return Math.max(value, 0.1F);
}
}
return value;
}
}

View file

@ -1,6 +1,5 @@
package com.minelittlepony.client.mixin;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.config.PonyLevel;
import com.minelittlepony.api.pony.DefaultPonySkinHelper;
@ -11,16 +10,28 @@ import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.UUID;
@Mixin(DefaultSkinHelper.class)
abstract class MixinDefaultSkinHelper {
@ModifyReturnValue(method = "getTexture()Lnet/minecraft/util/Identifier;", at = @At("RETURN"))
private static Identifier replaceDefaultSteveTexture(Identifier returnValue) {
return PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES ? DefaultPonySkinHelper.STEVE : returnValue;
@Inject(method = "getTexture()Lnet/minecraft/util/Identifier;",
at = @At("RETURN"),
cancellable = true)
private static void onGetTexture(CallbackInfoReturnable<Identifier> cir) {
if (PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES) {
cir.setReturnValue(DefaultPonySkinHelper.STEVE);
}
}
@ModifyReturnValue(method = "getSkinTextures(Ljava/util/UUID;)Lnet/minecraft/client/util/SkinTextures;", at = @At("RETURN"))
private static SkinTextures onGetTexture(SkinTextures returnValue) {
return PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES ? DefaultPonySkinHelper.getTextures(returnValue) : returnValue;
@Inject(method = "getSkinTextures(Ljava/util/UUID;)Lnet/minecraft/client/util/SkinTextures;",
at = @At("RETURN"),
cancellable = true)
private static void onGetTexture(UUID uuid, CallbackInfoReturnable<SkinTextures> cir) {
if (PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES) {
cir.setReturnValue(DefaultPonySkinHelper.getTextures(cir.getReturnValue()));
}
}
}

View file

@ -0,0 +1,17 @@
package com.minelittlepony.client.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.EntityRenderers;
import net.minecraft.entity.EntityType;
@Mixin(EntityRenderers.class)
public interface MixinEntityRenderers {
@Accessor("RENDERER_FACTORIES")
public static Map<EntityType<?>, EntityRendererFactory<?>> getRendererFactories() {
return null;
}
}

View file

@ -1,6 +1,7 @@
package com.minelittlepony.client.mixin;
import net.minecraft.client.render.*;
import net.minecraft.client.util.math.MatrixStack;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
@ -13,12 +14,12 @@ import com.minelittlepony.api.model.RenderPass;
@Mixin(GameRenderer.class)
abstract class MixinGameRenderer {
@Inject(method = "renderWorld", at = @At("HEAD"))
private void beforeRenderWorld(RenderTickCounter counter, CallbackInfo info) {
private void beforeRenderWorld(float tickDelta, long limitTime, MatrixStack matrices, CallbackInfo info) {
RenderPass.swap(RenderPass.WORLD);
}
@Inject(method = "renderWorld", at = @At("RETURN"))
private void afterRenderWorld(RenderTickCounter counter, CallbackInfo info) {
private void afterRenderWorld(float tickDelta, long limitTime, MatrixStack matrices, CallbackInfo info) {
RenderPass.swap(RenderPass.GUI);
}
}
@ -30,14 +31,7 @@ abstract class MixinWorldRenderer {
target = "net.minecraft.client.render.VertexConsumerProvider$Immediate.drawCurrentLayer()V",
ordinal = 0
))
private void onRender(
RenderTickCounter counter,
boolean renderBlockOutline,
Camera camera,
GameRenderer gameRenderer,
LightmapTextureManager lightmapTextureManager,
Matrix4f matrix4f,
Matrix4f matrix4f2, CallbackInfo info) {
private void onRender(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f positionMatrix, CallbackInfo info) {
RenderPass.swap(RenderPass.HUD);
}
}

View file

@ -2,19 +2,18 @@ package com.minelittlepony.client.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.minelittlepony.client.MineLittlePony;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.render.item.HeldItemRenderer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.model.json.ModelTransformationMode;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ModelTransformationMode;
import net.minecraft.world.World;
import net.minecraft.client.render.item.ItemRenderer;
@ -23,7 +22,7 @@ abstract class MixinHeldItemRenderer {
private static final String LivingEntity = "Lnet/minecraft/entity/LivingEntity;";
private static final String MatrixStack = "Lnet/minecraft/client/util/math/MatrixStack;";
private static final String ItemStack = "Lnet/minecraft/item/ItemStack;";
private static final String Mode = "Lnet/minecraft/item/ModelTransformationMode;";
private static final String Mode = "Lnet/minecraft/client/render/model/json/ModelTransformationMode;";
private static final String VertexConsumerProvider = "Lnet/minecraft/client/render/VertexConsumerProvider;";
private static final String World = "Lnet/minecraft/world/World;";
private static final String ItemRenderer = "Lnet/minecraft/client/render/item/ItemRenderer;";
@ -31,20 +30,18 @@ abstract class MixinHeldItemRenderer {
private static final String Boolean = "Z";
private static final String Int = "I";
@WrapOperation(method = "renderItem(" + LivingEntity + ItemStack + Mode + Boolean + MatrixStack + VertexConsumerProvider + Int + ")V",
@Redirect(method = "renderItem(" + LivingEntity + ItemStack + Mode + Boolean + MatrixStack + VertexConsumerProvider + Int + ")V",
at = @At(value = "INVOKE",
target = ItemRenderer + "renderItem(" + LivingEntity + ItemStack + Mode + Boolean + MatrixStack + VertexConsumerProvider + World + Int + Int + Int + ")V"))
private void wrapRenderItem(ItemRenderer target,
private void redirectRenderItem(ItemRenderer target,
@Nullable LivingEntity entity,
ItemStack stack,
ModelTransformationMode mode,
ItemStack item,
ModelTransformationMode transform,
boolean left,
MatrixStack matrices,
VertexConsumerProvider vertices,
MatrixStack stack,
VertexConsumerProvider renderContext,
@Nullable World world,
int light, int overlay, int seed, Operation<Void> operation) {
if (!MineLittlePony.getInstance().getRenderDispatcher().getMagicRenderer().renderItem(target, entity, stack, mode, left, matrices, vertices, world, light, overlay, seed, operation)) {
operation.call(target, entity, stack, mode, left, matrices, vertices, world, light, overlay, seed);
}
int lightUv, int overlayUv, int posLong) {
MineLittlePony.getInstance().getRenderDispatcher().getMagicRenderer().renderItem(target, entity, item, transform, left, stack, renderContext, world, lightUv, posLong);
}
}

View file

@ -1,33 +0,0 @@
package com.minelittlepony.client.mixin;
import net.minecraft.entity.*;
import net.minecraft.entity.player.PlayerEntity;
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 com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.minelittlepony.client.render.EquineRenderManager.RegistrationHandler;
import com.minelittlepony.client.render.EquineRenderManager.SyncedPony;
@Mixin(PlayerEntity.class)
abstract class MixinPlayerEntity implements RegistrationHandler {
private final SyncedPony syncedPony = new SyncedPony();
@Override
public SyncedPony getSyncedPony() {
return syncedPony;
}
@ModifyReturnValue(method = "getBaseDimensions(Lnet/minecraft/entity/EntityPose;)Lnet/minecraft/entity/EntityDimensions;", at = @At("RETURN"))
private EntityDimensions modifyEyeHeight(EntityDimensions dimensions, EntityPose pose) {
return getSyncedPony().modifyEyeHeight((PlayerEntity)(Object)this, dimensions, pose);
}
@Inject(method = "tick()V", at = @At("TAIL"))
private void onTick(CallbackInfo info) {
getSyncedPony().synchronize((PlayerEntity)(Object)this);
}
}

View file

@ -22,7 +22,8 @@ abstract class MixinPlayerMoveC2SPacket implements Packet<ServerPlayPacketListen
@Shadow @Final
protected boolean changeLook;
@Inject(method = "<init>(DDDFFZZZZ)V", at = @At("RETURN"))
@Inject(method = "<init>(DDDFFZZZ)V",
at = @At("RETURN"))
private void onInit(CallbackInfo info) {
if (changeLook) {
pitch = HorseCam.transformCameraAngle(pitch);

View file

@ -4,7 +4,6 @@ import com.minelittlepony.client.HorseCam;
import java.util.Set;
import net.minecraft.entity.player.PlayerPosition;
import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
@ -18,15 +17,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(PlayerPositionLookS2CPacket.class)
abstract class MixinPlayerPositionLookS2CPacket implements Packet<ClientPlayPacketListener> {
@Shadow @Mutable
private @Final PlayerPosition change;
private @Final float pitch;
@Shadow
private @Final Set<PositionFlag> relatives;
private @Final Set<PositionFlag> flags;
@Inject(method = "apply(Lnet/minecraft/network/listener/ClientPlayPacketListener;)V",
at = @At("HEAD"))
private void onApply(ClientPlayPacketListener clientPlayPacketListener, CallbackInfo info) {
if (!relatives.contains(PositionFlag.Y_ROT)) {
change = HorseCam.transformIncomingServerCameraAngle(change);
if (!flags.contains(PositionFlag.Y_ROT)) {
pitch = HorseCam.transformIncomingServerCameraAngle(pitch);
}
}
}

View file

@ -8,8 +8,6 @@ import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.render.block.entity.SkullBlockEntityModel;
import net.minecraft.client.render.block.entity.SkullBlockEntityRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.component.type.ProfileComponent;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import org.spongepowered.asm.mixin.Mixin;
@ -19,28 +17,40 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.client.render.blockentity.skull.PonySkullRenderer;
import com.mojang.authlib.GameProfile;
import org.jetbrains.annotations.Nullable;
@Mixin(SkullBlockEntityRenderer.class)
abstract class MixinSkullBlockEntityRenderer implements BlockEntityRenderer<SkullBlockEntity> {
@Inject(method = "renderSkull", at = @At("HEAD"), cancellable = true)
@Inject(method = "renderSkull("
+ "Lnet/minecraft/util/math/Direction;"
+ "F"
+ "F"
+ "Lnet/minecraft/client/util/math/MatrixStack;"
+ "Lnet/minecraft/client/render/VertexConsumerProvider;"
+ "I"
+ "Lnet/minecraft/client/render/block/entity/SkullBlockEntityModel;"
+ "Lnet/minecraft/client/render/RenderLayer;"
+ ")V", at = @At("HEAD"), cancellable = true)
private static void onRenderSkull(@Nullable Direction direction,
float yaw, float animationProgress,
MatrixStack matrices, VertexConsumerProvider vertices,
int light,
float angle, float poweredTicks,
MatrixStack stack, VertexConsumerProvider renderContext, int lightUv,
SkullBlockEntityModel model, RenderLayer layer,
CallbackInfo info) {
if (!info.isCancelled() && PonySkullRenderer.INSTANCE.renderSkull(direction, yaw, animationProgress, matrices, vertices, layer, light)) {
if (!info.isCancelled() && PonySkullRenderer.renderSkull(direction, angle, poweredTicks, stack, renderContext, layer, lightUv)) {
info.cancel();
}
}
@Inject(method = "getRenderLayer(Lnet/minecraft/block/SkullBlock$SkullType;Lnet/minecraft/component/type/ProfileComponent;Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/RenderLayer;", at = @At("HEAD"), cancellable = true)
private static void onGetRenderLayer(SkullBlock.SkullType skullType, @Nullable ProfileComponent profile, @Nullable Identifier texture, CallbackInfoReturnable<RenderLayer> info) {
@Inject(method = "getRenderLayer("
+ "Lnet/minecraft/block/SkullBlock$SkullType;"
+ "Lcom/mojang/authlib/GameProfile;"
+ ")Lnet/minecraft/client/render/RenderLayer;", at = @At("HEAD"), cancellable = true)
private static void onGetRenderLayer(SkullBlock.SkullType skullType, @Nullable GameProfile profile, CallbackInfoReturnable<RenderLayer> info) {
if (!info.isCancelled()) {
RenderLayer result = PonySkullRenderer.INSTANCE.getSkullRenderLayer(skullType, profile);
RenderLayer result = PonySkullRenderer.getSkullRenderLayer(skullType, profile);
if (result != null) {
info.setReturnValue(result);
}

View file

@ -1,12 +1,12 @@
package com.minelittlepony.client.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Set;
import java.util.Arrays;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumers;
@ -25,12 +25,15 @@ abstract class MixinVertextConsumers {
}
}
@ModifyVariable(method = "union([" + T + ")" + T, at = @At("HEAD"), argsOnly = true)
private static VertexConsumer[] onUnion(VertexConsumer[] delegates) {
Set<VertexConsumer> set = new ObjectArraySet<>(delegates.length);
for (VertexConsumer delegate : delegates) {
set.add(delegate);
@Inject(method = "union([" + T + ")" + T, at = @At("HEAD"), cancellable = true)
private static void onUnion(VertexConsumer[] delegates, CallbackInfoReturnable<VertexConsumer> info) {
int oldLength = delegates.length;
delegates = Arrays.stream(delegates).distinct().toArray(VertexConsumer[]::new);
if (delegates.length == 1) {
info.setReturnValue(delegates[0]);
} else if (delegates.length != oldLength) {
info.setReturnValue(new VertexConsumers.Union(delegates));
}
return set.toArray(VertexConsumer[]::new);
}
}

View file

@ -1,30 +1,28 @@
package com.minelittlepony.client.model;
import com.minelittlepony.api.model.*;
import com.minelittlepony.api.events.PonyModelPrepareCallback;
import com.minelittlepony.api.pony.meta.SizePreset;
import com.minelittlepony.client.render.entity.state.PonyRenderState;
import com.minelittlepony.client.transform.PonyTransformation;
import com.minelittlepony.mson.util.RenderList;
import com.minelittlepony.client.util.render.RenderList;
import com.minelittlepony.util.MathUtil;
import com.minelittlepony.util.MathUtil.Angles;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.state.PlayerEntityRenderState;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.EntityPose;
import net.minecraft.item.consume.UseAction;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
/**
* Foundation class for all types of ponies.
*/
public abstract class AbstractPonyModel<T extends PonyRenderState> extends ClientPonyModel<T> {
public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPonyModel<T> {
public static final float NECK_X = 0.166F;
public static final float LEG_SNEAKING_PITCH_ADJUSTMENT = 0.4F;
public static final float BODY_RIDING_PITCH = MathHelper.PI * 3.8F;
@ -33,6 +31,7 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
public static final Pivot ORIGIN = new Pivot(0, 0, 0);
public static final Pivot HEAD_SNEAKING = new Pivot(0, 6, -2);
public static final Pivot HEAD_SLEEPING = new Pivot(1, 2, 0);
public static final Pivot BODY_SNEAKING = new Pivot(0, 7, -4);
public static final Pivot BODY_RIDING = new Pivot(0, 1, 4);
public static final Pivot FONT_LEGS_SLEEPING = new Pivot(0, 2, 6);
@ -40,128 +39,149 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
protected final ModelPart neck;
public final RenderList helmetRenderList;
protected final RenderList neckRenderList;
public final RenderList headRenderList;
protected final RenderList bodyRenderList;
protected final RenderList vestRenderList;
protected final RenderList legsRenderList;
protected final RenderList sleevesRenderList;
protected final RenderList mainRenderList;
private final List<SubModel<? super T>> parts = new ArrayList<>();
private final List<SubModel> parts = new ArrayList<>();
public AbstractPonyModel(ModelPart tree, boolean smallArms) {
super(tree, smallArms);
public AbstractPonyModel(ModelPart tree) {
super(tree);
neck = tree.getChild("neck");
mainRenderList = RenderList.of()
.add(withStage(BodyPart.BODY, bodyRenderList = RenderList.of(body).add(body::rotate)))
.add(withStage(BodyPart.NECK, neckRenderList = RenderList.of(neck)))
.add(withStage(BodyPart.HEAD, headRenderList = RenderList.of(head)))
.add(withStage(BodyPart.LEGS, legsRenderList = RenderList.of().add(leftArm, rightArm, leftLeg, rightLeg)));
.add(withStage(BodyPart.LEGS, legsRenderList = RenderList.of().add(leftArm, rightArm, leftLeg, rightLeg)))
.add(withStage(BodyPart.LEGS, sleevesRenderList = RenderList.of().add(leftSleeve, rightSleeve, leftPants, rightPants)))
.add(withStage(BodyPart.BODY, vestRenderList = RenderList.of(jacket)))
.add(withStage(BodyPart.HEAD, helmetRenderList = RenderList.of(hat)));
}
protected <P extends SubModel<? super T>> P addPart(P part) {
protected <P extends SubModel> P addPart(P part) {
parts.add(part);
return part;
}
@SuppressWarnings({"deprecation"})
protected RenderList withStage(BodyPart part, RenderList action) {
return (stack, vertices, overlay, light, color) -> {
stack.push();
if (currentState != null) {
transform(currentState, part, stack);
}
action.accept(stack, vertices, overlay, light, color);
stack.pop();
protected RenderList forPart(Supplier<SubModel> part) {
return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
part.get().renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes);
};
}
protected RenderList forPart(SubModel part) {
return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
part.renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes);
};
}
@Override
public final void render(MatrixStack stack, VertexConsumer vertices, int overlay, int light, int color) {
mainRenderList.accept(stack, vertices, overlay, light, color);
public void render(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) {
mainRenderList.accept(stack, vertices, overlayUv, lightUv, red, green, blue, alpha);
}
protected void setModelVisibilities(T state) {
protected RenderList withStage(BodyPart part, RenderList action) {
return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
stack.push();
transform(part, stack);
action.accept(stack, vertices, overlayUv, lightUv, red, green, blue, alpha);
stack.pop();
};
}
/**
* Sets the model's various rotation angles.
*/
@Override
public final void setAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) {
attributes.checkRainboom(entity, this, animationProgress);
PonyModelPrepareCallback.EVENT.invoker().onPonyModelPrepared(entity, this, ModelAttributes.Mode.OTHER);
super.setAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch);
resetPivot(head, neck, leftArm, rightArm, leftLeg, rightLeg);
hat.visible = head.visible && !state.attributes.isHorsey;
if (state.attributes.isHorsey) {
neck.visible = head.visible;
} else {
neck.hidden = !head.visible;
}
parts.forEach(part -> part.setVisible(body.visible, state));
setModelAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch);
leftSleeve.copyTransform(leftArm);
rightSleeve.copyTransform(rightArm);
leftPants.copyTransform(leftLeg);
rightPants.copyTransform(rightLeg);
jacket.copyTransform(body);
hat.copyTransform(head);
}
@SuppressWarnings({"unchecked", "deprecation"})
public void copyTransforms(BipedEntityModel<PlayerEntityRenderState> model) {
super.copyTransforms(model);
if (model instanceof AbstractPonyModel m) {
((AbstractPonyModel<T>)m).currentState = currentState;
}
}
protected void setModelAngles(T entity) {
float pitch = entity.attributes.motionPitch * MathHelper.RADIANS_PER_DEGREE;
protected void setModelAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) {
float pitch = attributes.motionPitch * MathHelper.RADIANS_PER_DEGREE;
head.setAngles(
MathHelper.clamp(entity.attributes.isSleeping ? 0.1f : entity.pitch / 57.29578F, -1.25f - pitch, 0.5f - pitch),
entity.attributes.isSleeping ? (Math.signum(MathHelper.wrapDegrees(entity.yawDegrees)) * 1.3F) : entity.yawDegrees * MathHelper.RADIANS_PER_DEGREE,
MathHelper.clamp(attributes.isSleeping ? 0.1f : headPitch / 57.29578F, -1.25f - pitch, 0.5f - pitch),
attributes.isSleeping ? (Math.signum(MathHelper.wrapDegrees(headYaw)) * 1.3F) : headYaw * MathHelper.RADIANS_PER_DEGREE,
0
);
float wobbleAmount = entity.wobbleAmount * getWobbleAmplitude(entity);
float wobbleAmount = getWobbleAmount();
body.yaw = wobbleAmount;
neck.yaw = wobbleAmount;
rotateLegs(entity);
rotateLegs(limbAngle, limbSpeed, animationProgress, entity);
if (!entity.attributes.isSwimming && !entity.attributes.isGoingFast) {
alignArmForAction(entity, getArm(Arm.LEFT), entity.leftArmPose, entity.rightArmPose, 1);
alignArmForAction(entity, getArm(Arm.RIGHT), entity.rightArmPose, entity.leftArmPose, -1);
if (onSetModelAngles != null) {
onSetModelAngles.poseModel(this, limbAngle, limbSpeed, animationProgress, entity);
}
if (!attributes.isSwimming && !attributes.isGoingFast) {
holdItem(limbSpeed);
}
swingItem(entity);
if (entity.attributes.isCrouching) {
ponyCrouch(entity);
} else if (entity.isInPose(EntityPose.SITTING)) {
if (attributes.isCrouching) {
ponyCrouch();
} else if (riding) {
ponySit();
} else {
adjustBody(entity, 0, ORIGIN);
adjustBody(0, ORIGIN);
if (!entity.attributes.isLyingDown) {
animateBreathing(entity);
if (!attributes.isLyingDown) {
animateBreathing(animationProgress);
}
if (entity.attributes.isSwimmingRotated) {
if (attributes.isSwimmingRotated) {
rightLeg.pivotZ -= 1.5F;
leftLeg.pivotZ -= 1.5F;
}
}
if (entity.attributes.isLyingDown) {
if (attributes.isLyingDown) {
ponySleep();
}
if (entity.attributes.isHorsey) {
if (attributes.isHorsey) {
head.pivotY -= 3;
head.pivotZ -= 2;
head.pitch = 0.5F;
}
parts.forEach(part -> part.setPartAngles(entity, wobbleAmount));
parts.forEach(part -> part.setPartAngles(attributes, limbAngle, limbSpeed, wobbleAmount, animationProgress));
}
public void setHeadRotation(float animationProgress, float yaw, float pitch) {
head.yaw = yaw * MathHelper.RADIANS_PER_DEGREE;
head.pitch = pitch * MathHelper.RADIANS_PER_DEGREE;
hat.copyTransform(head);
}
/**
* Aligns legs to a sneaky position.
*/
protected void ponyCrouch(T state) {
adjustBody(state, BODY_SNEAKING_PITCH, BODY_SNEAKING);
protected void ponyCrouch() {
adjustBody(BODY_SNEAKING_PITCH, BODY_SNEAKING);
HEAD_SNEAKING.set(head);
rightArm.pitch -= LEG_SNEAKING_PITCH_ADJUSTMENT;
@ -175,6 +195,8 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
rightLeg.pitch = MathUtil.Angles._90_DEG;
leftLeg.pitch = MathUtil.Angles._90_DEG;
HEAD_SLEEPING.set(head);
FONT_LEGS_SLEEPING.add(rightArm);
FONT_LEGS_SLEEPING.add(leftArm);
BACK_LEGS_SLEEPING.add(rightLeg);
@ -182,9 +204,14 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
}
protected void ponySit() {
adjustBodyComponents(BODY_RIDING_PITCH, BODY_RIDING);
neck.setPivot(NECK_X, 0, 0);
head.setPivot(0, 0, 0);
adjustBodyComponents(BODY_RIDING_PITCH * (attributes.isRidingInteractive ? 2 : 1), BODY_RIDING);
if (attributes.isRidingInteractive) {
neck.setPivot(NECK_X, 0, -4);
head.setPivot(0, -2, -5);
} else {
neck.setPivot(NECK_X, 0, 0);
head.setPivot(0, 0, 0);
}
leftLeg.pivotZ = 14;
leftLeg.pivotY = 17;
@ -204,6 +231,26 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
leftArm.pitch += body.pitch;
rightArm.roll = MathHelper.PI * 0.06f;
rightArm.pitch += body.pitch;
if (attributes.isRidingInteractive) {
leftLeg.yaw = MathHelper.PI / 15;
leftLeg.pitch = MathHelper.PI / 9;
leftLeg.pivotZ = 10;
leftLeg.pivotY = 7;
rightLeg.yaw = -MathHelper.PI / 15;
rightLeg.pitch = MathHelper.PI / 9;
rightLeg.pivotZ = 10;
rightLeg.pivotY = 7;
leftArm.pitch = MathHelper.PI / 6;
rightArm.pitch = MathHelper.PI / 6;
leftArm.roll *= 2;
rightArm.roll *= 2;
}
}
/**
@ -213,11 +260,11 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
* Takes the same parameters as {@link AbstractPonyModel.setRotationAndAngles}
*
*/
protected void rotateLegs(T state) {
if (state.attributes.isSwimming) {
rotateLegsSwimming(state, state.limbAmplitudeInverse, state.limbAmplitudeMultiplier, state.age);
protected void rotateLegs(float move, float swing, float ticks, T entity) {
if (attributes.isSwimming) {
rotateLegsSwimming(move, swing, ticks, entity);
} else {
rotateLegsOnGround(state, state.limbAmplitudeInverse, state.limbAmplitudeMultiplier, state.age);
rotateLegsOnGround(move, swing, ticks, entity);
}
float sin = MathHelper.sin(body.yaw) * 5;
@ -226,8 +273,8 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
rightArm.pivotZ = 2 + sin;
leftArm.pivotZ = 2 - sin;
float legRPX = state.attributes.getMainInterpolator().interpolate("legOffset", cos - state.legOutset - 0.001F, 2);
if (state.attributes.isHorsey) {
float legRPX = attributes.getMainInterpolator().interpolate("legOffset", cos - getLegOutset() - 0.001F, 2);
if (attributes.isHorsey) {
legRPX += 2;
}
@ -240,7 +287,7 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
rightArm.yaw += body.yaw;
leftArm.yaw += body.yaw;
if (state.attributes.isHorsey) {
if (attributes.isHorsey) {
rightArm.pivotZ = leftArm.pivotZ = -1;
rightArm.pivotY = leftArm.pivotY = 6;
rightLeg.pivotZ = leftLeg.pivotZ = 19;
@ -253,13 +300,14 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
*
* Takes the same parameters as {@link AbstractPonyModel.setRotationAndAngles}
*/
protected void rotateLegsSwimming(T state, @Deprecated float move, @Deprecated float swing, @Deprecated float ticks) {
float lerp = state.isInPose(EntityPose.SWIMMING) ? (float)state.attributes.motionLerp : 1;
protected void rotateLegsSwimming(float move, float swing, float ticks, T entity) {
float legLeft = (MathUtil.Angles._90_DEG + MathHelper.sin((state.limbFrequency / 3) + 2 * MathHelper.PI/3) / 2) * lerp;
float lerp = entity.isSwimming() ? (float)attributes.motionLerp : 1;
float left = (MathUtil.Angles._90_DEG + MathHelper.sin((state.limbFrequency / 3) + 2 * MathHelper.PI) / 2) * lerp;
float right = (MathUtil.Angles._90_DEG + MathHelper.sin(state.limbFrequency / 3) / 2) * lerp;
float legLeft = (MathUtil.Angles._90_DEG + MathHelper.sin((move / 3) + 2 * MathHelper.PI/3) / 2) * lerp;
float left = (MathUtil.Angles._90_DEG + MathHelper.sin((move / 3) + 2 * MathHelper.PI) / 2) * lerp;
float right = (MathUtil.Angles._90_DEG + MathHelper.sin(move / 3) / 2) * lerp;
leftArm.setAngles(-left, -left/2, left/2);
rightArm.setAngles(-right, right/2, -right/2);
@ -271,15 +319,15 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
* Rotates legs in quopy fashion for walking.
*
*/
protected void rotateLegsOnGround(T state, float move, float swing, float ticks) {
protected void rotateLegsOnGround(float move, float swing, float ticks, T entity) {
float angle = MathHelper.PI * (float) Math.pow(swing, 16);
float baseRotation = state.limbFrequency * 0.6662F; // magic number ahoy
float scale = state.limbAmplitudeMultiplier / 4;
float baseRotation = move * 0.6662F; // magic number ahoy
float scale = swing / 4;
float rainboomLegLotation = state.attributes.getMainInterpolator().interpolate(
float rainboomLegLotation = attributes.getMainInterpolator().interpolate(
"rainboom_leg_rotation",
state.attributes.isGoingFast ? 1 : 0,
attributes.isGoingFast ? 1 : 0,
5
);
float yAngle = 0.2F * rainboomLegLotation;
@ -290,6 +338,26 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
rightLeg.setAngles(MathHelper.lerp(rainboomLegLotation, MathHelper.cos(baseRotation + angle / 5) * scale, MathUtil.Angles._90_DEG * rainboomLegLotation), -yAngle, rightLeg.roll);
}
protected float getLegOutset() {
if (attributes.isLyingDown) {
return 3.6f;
}
if (attributes.isCrouching) {
return 1;
}
return 5;
}
/**
* Adjusts legs as if holding an item. Delegates to the correct arm/leg/limb as necessary.
*/
protected void holdItem(float limbSpeed) {
alignArmForAction(getArm(Arm.LEFT), leftArmPose, rightArmPose, limbSpeed, 1);
alignArmForAction(getArm(Arm.RIGHT), rightArmPose, leftArmPose, limbSpeed, -1);
}
@Override
public ModelPart getBodyPart(BodyPart part) {
switch (part) {
@ -310,25 +378,25 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
* @param pose The post to align to
* @param limbSpeed Degree to which each 'limb' swings.
*/
protected void alignArmForAction(T state, ModelPart arm, ArmPose pose, ArmPose complement, float sigma) {
protected void alignArmForAction(ModelPart arm, ArmPose pose, ArmPose complement, float limbSpeed, float sigma) {
switch (pose) {
case ITEM:
arm.yaw = 0;
boolean both = pose == complement;
if (state.attributes.shouldLiftArm(pose, complement, sigma)) {
if (attributes.shouldLiftArm(pose, complement, sigma)) {
float swag = 1;
if (!state.attributes.isFlying && both) {
swag -= (float)Math.pow(state.limbAmplitudeMultiplier, 2);
if (!getAttributes().isFlying && both) {
swag -= (float)Math.pow(limbSpeed, 2);
}
float mult = 1 - swag/2;
arm.pitch = arm.pitch * mult - (MathHelper.PI / 10) * swag;
arm.roll = -sigma * (MathHelper.PI / 15);
arm.roll += 0.3F * -state.limbAmplitudeMultiplier * sigma;
arm.roll += 0.3F * -limbSpeed * sigma;
if (state.attributes.isCrouching) {
if (attributes.isCrouching) {
arm.pivotX -= sigma * 2;
}
}
@ -340,52 +408,52 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
case BLOCK:
arm.pitch = (arm.pitch / 2 - 0.9424779F) - 0.3F;
arm.yaw = sigma * MathHelper.PI / 9;
arm.roll += 0.3F * -state.limbAmplitudeMultiplier * sigma;
arm.roll += 0.3F * -limbSpeed * sigma;
if (complement == pose) {
arm.yaw -= sigma * MathHelper.PI / 18;
}
arm.pivotX += sigma;
arm.pivotZ += 3;
if (state.attributes.isCrouching) {
if (attributes.isCrouching) {
arm.pivotY += 4;
}
break;
case BOW_AND_ARROW:
aimBow(state, arm);
aimBow(arm, limbSpeed);
break;
case CROSSBOW_HOLD:
aimBow(state, arm);
aimBow(arm, limbSpeed);
arm.pitch = head.pitch - MathUtil.Angles._90_DEG;
arm.yaw = head.yaw + 0.06F;
break;
case CROSSBOW_CHARGE:
aimBow(state, arm);
aimBow(arm, limbSpeed);
arm.pitch = -0.8F;
arm.yaw = head.yaw + 0.06F;
arm.roll += 0.3F * -state.limbAmplitudeMultiplier * sigma;
arm.roll += 0.3F * -limbSpeed * sigma;
break;
case THROW_SPEAR:
arm.pitch = MathUtil.Angles._90_DEG * 2;
arm.roll += (0.3F * -state.limbAmplitudeMultiplier + 0.6F) * sigma;
arm.roll += (0.3F * -limbSpeed + 0.6F) * sigma;
arm.pivotY ++;
break;
case SPYGLASS:
float addedPitch = state.isInSneakingPose ? -0.2617994F : 0;
float minPitch = state.isInSneakingPose ? -1.8F : -2.4F;
float addedPitch = sneaking ? -0.2617994F : 0;
float minPitch = sneaking ? -1.8F : -2.4F;
arm.pitch = MathHelper.clamp(head.pitch - 1.9198622F - addedPitch, minPitch, 3.3F);
arm.yaw = head.yaw;
if (state.isInSneakingPose) {
if (sneaking) {
arm.pivotY += 9;
arm.pivotX -= 6 * sigma;
arm.pivotZ -= 2;
}
if (state.attributes.size == SizePreset.TALL) {
if (getSize() == SizePreset.TALL) {
arm.pivotY += 1;
}
if (state.attributes.size == SizePreset.FOAL) {
if (getSize() == SizePreset.FOAL) {
arm.pivotY -= 2;
}
@ -394,36 +462,38 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
arm.pitch = MathHelper.clamp(head.pitch, -0.55f, 1.2f) - 1.7835298f;
arm.yaw = head.yaw - 0.1235988f * sigma;
arm.pivotY += 3;
arm.roll += 0.3F * -state.limbAmplitudeMultiplier * sigma;
arm.roll += 0.3F * -limbSpeed * sigma;
break;
case BRUSH:
arm.pitch = arm.pitch * 0.5f - 0.62831855f;
arm.yaw = 0;
arm.roll += 0.3F * -state.limbAmplitudeMultiplier * sigma;
arm.roll += 0.3F * -limbSpeed * sigma;
break;
default:
break;
}
}
protected final void aimBow(T state, ModelPart arm) {
arm.pitch = MathUtil.Angles._270_DEG + head.pitch + (MathHelper.sin(state.limbAmplitudeMultiplier * 0.067F) * 0.05F);
protected void aimBow(ModelPart arm, float limbSpeed) {
arm.pitch = MathUtil.Angles._270_DEG + head.pitch + (MathHelper.sin(limbSpeed * 0.067F) * 0.05F);
arm.yaw = head.yaw - 0.06F;
arm.roll = MathHelper.cos(state.limbAmplitudeMultiplier * 0.09F) * 0.05F + 0.05F;
arm.roll = MathHelper.cos(limbSpeed * 0.09F) * 0.05F + 0.05F;
if (state.isInSneakingPose) {
if (sneaking) {
arm.pivotY += 4;
}
}
/**
* Animates arm swinging. Delegates to the correct arm/leg/limb as necessary.
* Animates arm swinging. Delegates to the correct arm/leg/limb as neccessary.
*
* @param entity The entity we are being called for.
*/
protected final void swingItem(T state) {
if (state.handSwingProgress > 0 && !state.attributes.isLyingDown) {
swingArm(state, getArm(state.preferredArm));
protected void swingItem(T entity) {
if (getSwingAmount() > 0 && !attributes.isLyingDown) {
Arm mainSide = getPreferredArm(entity);
swingArm(getArm(mainSide));
}
}
@ -432,11 +502,11 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
*
* @param arm The arm to swing
*/
protected final void swingArm(T state, ModelPart arm) {
float swing = 1 - (float)Math.pow(1 - state.handSwingProgress, 3);
protected void swingArm(ModelPart arm) {
float swing = 1 - (float)Math.pow(1 - getSwingAmount(), 3);
float deltaX = MathHelper.sin(swing * MathHelper.PI);
float deltaZ = MathHelper.sin(state.handSwingProgress * MathHelper.PI);
float deltaZ = MathHelper.sin(getSwingAmount() * MathHelper.PI);
float deltaAim = deltaZ * (0.7F - head.pitch) * 0.75F;
@ -451,26 +521,26 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
* @param animationProgress Total whole and partial ticks since the entity's existence.
* Used in animations together with {@code swing} and {@code move}.
*/
protected void animateBreathing(T state) {
float cos = MathHelper.cos(state.age * 0.09F) * 0.05F + 0.05F;
float sin = MathHelper.sin(state.age * 0.067F) * 0.05F;
protected void animateBreathing(float animationProgress) {
float cos = MathHelper.cos(animationProgress * 0.09F) * 0.05F + 0.05F;
float sin = MathHelper.sin(animationProgress * 0.067F) * 0.05F;
if (state.attributes.shouldLiftArm(state.rightArmPose, state.leftArmPose, -1)) {
if (attributes.shouldLiftArm(rightArmPose, leftArmPose, -1)) {
ModelPart arm = getArm(Arm.RIGHT);
arm.roll += cos;
arm.pitch += sin;
}
if (state.attributes.shouldLiftArm(state.leftArmPose, state.rightArmPose, 1)) {
if (attributes.shouldLiftArm(leftArmPose, rightArmPose, 1)) {
ModelPart arm = getArm(Arm.LEFT);
arm.roll += cos;
arm.pitch += sin;
}
}
protected void adjustBody(T state, float pitch, Pivot pivot) {
protected void adjustBody(float pitch, Pivot pivot) {
adjustBodyComponents(pitch, pivot);
if (!state.attributes.isHorsey) {
if (!attributes.isHorsey) {
neck.setPivot(NECK_X + pitch, pivot.y(), pivot.z());
rightLeg.pivotY = FRONT_LEGS_Y;
leftLeg.pivotY = FRONT_LEGS_Y;
@ -486,49 +556,92 @@ public abstract class AbstractPonyModel<T extends PonyRenderState> extends Clien
body.pivotZ = pivot.z();
}
@Override
public float getRiderYOffset() {
switch ((SizePreset)getSize()) {
case NORMAL: return 0.4F;
case FOAL:
case TALL:
case BULKY:
default: return 0.25F;
}
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
neck.visible = visible;
hat.visible = false;
hat.visible &= !attributes.isHorsey;
parts.forEach(part -> part.setVisible(visible, attributes));
}
public void positionheldItem(T state, Arm arm, MatrixStack matrices) {
@Override
public final void setArmAngle(Arm arm, MatrixStack matrices) {
super.setArmAngle(arm, matrices);
positionheldItem(arm, matrices);
}
protected void positionheldItem(Arm arm, MatrixStack matrices) {
float left = arm == Arm.LEFT ? -1 : 1;
UseAction action = state.getHeldItem(arm).action;
UseAction action = getAttributes().heldStack.getUseAction();
if (action == UseAction.SPYGLASS && state.attributes.itemUseTime > 0) {
return;
if (action == UseAction.SPYGLASS && getAttributes().itemUseTime > 0) {
Arm main = getAttributes().mainArm;
if (getAttributes().activeHand == Hand.OFF_HAND) {
main = main.getOpposite();
}
if (main == arm) {
matrices.translate(left * -0.05F, 0.5F, 0.2F);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-60));
return;
}
}
if (getAttributes().isRidingInteractive) {
matrices.translate(left / 10, -0.2F, -0.5F);
}
matrices.translate(-left * 0.1F, 0.45F, 0);
if (action == UseAction.BLOCK && state.attributes.itemUseTime == 0) {
if (getAttributes().heldStack.getUseAction() == UseAction.BLOCK && getAttributes().itemUseTime == 0) {
matrices.translate(left * 0.02F, -0.25F, 0);
}
}
@Override
public void transform(T state, BodyPart part, MatrixStack stack) {
public void transform(BodyPart part, MatrixStack stack) {
if (state.attributes.isHorsey) {
if (attributes.isHorsey) {
stack.translate(0, 0.1F, 0);
}
if (state.attributes.isSleeping || state.attributes.isRiptide) {
if (attributes.isSleeping || attributes.isRiptide) {
stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180));
}
if (state.attributes.isLyingDown && !state.attributes.isSleeping) {
boolean crouching = attributes.isCrouching;
if (attributes.isLyingDown && !attributes.isSleeping) {
stack.translate(0, 1.35F, 0);
attributes.isCrouching = sneaking;
}
if (state.attributes.isHorsey && part == BodyPart.BODY) {
stack.scale(1.5F, 1, 1.5F);
if (attributes.isHorsey) {
if (part == BodyPart.BODY) {
stack.scale(1.5F, 1, 1.5F);
}
neck.visible = head.visible;
} else {
neck.hidden = !head.visible;
}
PonyTransformation.forSize(state.attributes.size).transform(state.attributes, part, stack);
PonyTransformation.forSize(getSize()).transform(this, part, stack);
attributes.isCrouching = crouching;
}
}

View file

@ -2,12 +2,19 @@ package com.minelittlepony.client.model;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.*;
import net.minecraft.client.render.entity.state.PlayerEntityRenderState;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm;
import net.minecraft.util.Hand;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.events.PonyModelPrepareCallback;
import com.minelittlepony.api.model.*;
import com.minelittlepony.client.render.entity.state.PonyRenderState;
import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.api.pony.meta.Size;
import com.minelittlepony.api.pony.meta.SizePreset;
import com.minelittlepony.mson.api.model.biped.MsonPlayer;
/**
* The raw pony model without any implementations.
@ -16,43 +23,73 @@ import com.minelittlepony.client.render.entity.state.PonyRenderState;
*
* Modders can extend this class to make their own pony models if they wish.
*/
public abstract class ClientPonyModel<T extends PonyRenderState> extends PlayerEntityModel implements PonyModel<T> {
public abstract class ClientPonyModel<T extends LivingEntity> extends MsonPlayer<T> implements PonyModel<T> {
/**
* The model attributes.
*/
protected ModelAttributes attributes = new ModelAttributes();
@Nullable
protected PosingCallback<T> onSetModelAngles;
@Deprecated
@Nullable
protected T currentState;
public ClientPonyModel(ModelPart tree, boolean smallArms) {
super(tree, smallArms);
public ClientPonyModel(ModelPart tree) {
super(tree);
}
public void onSetModelAngles(PosingCallback<T> callback) {
onSetModelAngles = callback;
}
/**
* Sets the model's various rotation angles.
*/
@SuppressWarnings("unchecked")
protected Arm getPreferredArm(T livingEntity) {
Arm arm = livingEntity.getMainArm();
return livingEntity.preferredHand == Hand.MAIN_HAND ? arm : arm.getOpposite();
}
@Override
public final void setAngles(PlayerEntityRenderState entity) {
currentState = (T)entity;
super.setAngles((PlayerEntityRenderState)entity);
public void updateLivingState(T entity, Pony pony, ModelAttributes.Mode mode) {
child = entity.isBaby();
attributes.updateLivingState(entity, pony, mode);
PonyModelPrepareCallback.EVENT.invoker().onPonyModelPrepared(entity, this, mode);
sneaking = attributes.isCrouching && !attributes.isLyingDown;
riding = attributes.isSitting;
}
setModelVisibilities((T)entity);
setModelAngles((T)entity);
@Override
public final void copyAttributes(BipedEntityModel<T> other) {
copyStateTo(other);
}
if (onSetModelAngles != null) {
onSetModelAngles.poseModel(this, (T)entity);
/**
* Copies this model's attributes into the passed model.
*/
@Override
public void copyStateTo(EntityModel<T> model) {
super.copyStateTo(model);
if (model instanceof ClientPonyModel) {
((ClientPonyModel<T>)model).attributes = attributes;
}
}
protected void setModelVisibilities(T state) {
@Override
public final ModelAttributes getAttributes() {
return attributes;
}
protected void setModelAngles(T entity) {
@Override
public Size getSize() {
return child ? SizePreset.FOAL : PonyModel.super.getSize();
}
@Override
public void setMetadata(PonyData meta) {
attributes.metadata = meta;
}
@Override
public float getSwingAmount() {
return handSwingProgress;
}
@Override
@ -65,8 +102,18 @@ public abstract class ClientPonyModel<T extends PonyRenderState> extends PlayerE
return side == Arm.LEFT ? leftLeg : rightLeg;
}
@Override
public ArmPose getArmPoseForSide(Arm side) {
return side == Arm.RIGHT ? rightArmPose : leftArmPose;
}
@Override
public void setHatVisible(boolean visible) {
}
static void resetPivot(ModelPart part) {
part.setPivot(part.getDefaultTransform().pivotX(), part.getDefaultTransform().pivotY(), part.getDefaultTransform().pivotZ());
part.setPivot(part.getDefaultTransform().pivotX, part.getDefaultTransform().pivotY, part.getDefaultTransform().pivotZ);
}
static void resetPivot(ModelPart...parts) {
@ -75,7 +122,7 @@ public abstract class ClientPonyModel<T extends PonyRenderState> extends PlayerE
}
}
public interface PosingCallback<S extends PonyRenderState> {
void poseModel(ClientPonyModel<S> model, S state);
public interface PosingCallback<T extends LivingEntity> {
void poseModel(ClientPonyModel<T> model, float move, float swing, float ticks, T entity);
}
}

View file

@ -1,17 +1,18 @@
package com.minelittlepony.client.model;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.Model.SinglePartModel;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.ArmorStandEntityModel;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.VexEntity;
import net.minecraft.entity.passive.*;
import net.minecraft.util.Identifier;
import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.api.model.gear.*;
import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.client.model.entity.*;
import com.minelittlepony.client.model.entity.race.*;
@ -28,7 +29,7 @@ import java.util.function.BiFunction;
import java.util.stream.Stream;
public final class ModelType {
private static final Map<Race, PlayerModelKey<?>> PLAYER_MODELS = new HashMap<>();
private static final Map<Race, PlayerModelKey<?, ?>> PLAYER_MODELS = new HashMap<>();
private static final Map<Wearable, GearModelKey<? extends Gear>> GEAR_MODELS = new HashMap<>();
public static final ModelKey<DJPon3EarsModel> DJ_PON_3 = register("dj_pon_three", DJPon3EarsModel::new);
@ -36,64 +37,63 @@ public final class ModelType {
public static final ModelKey<WitchPonyModel> WITCH = register("witch", WitchPonyModel::new);
public static final ModelKey<ZomponyModel<?>> ZOMBIE = register("zombie", ZomponyModel::new);
public static final ModelKey<PiglinPonyModel> PIGLIN = register("piglin", PiglinPonyModel::new);
public static final ModelKey<AlicornModel<?>> SKELETON = register("skeleton", tree -> new AlicornModel<>(tree, false));
public static final ModelKey<SinglePartModel> BOGGED_MUSHROOMS = register("bogged_mushrooms", tree -> new SinglePartModel(tree, RenderLayer::getEntityTranslucent));
public static final ModelKey<AlicornModel<?>> SKELETON_CLOTHES = register("skeleton_clothes", tree -> new AlicornModel<>(tree, false));
public static final ModelKey<ChangelingModel<?>> PILLAGER = register("pillager", tree -> new ChangelingModel<>(tree, false));
public static final ModelKey<SkeleponyModel<?>> SKELETON = register("skeleton", SkeleponyModel::new);
public static final ModelKey<SkeleponyModel<?>> SKELETON_CLOTHES = register("skeleton_clothes", SkeleponyModel::new);
public static final ModelKey<PillagerPonyModel<?>> PILLAGER = register("pillager", PillagerPonyModel::new);
public static final ModelKey<IllagerPonyModel<?>> ILLAGER = register("illager", IllagerPonyModel::new);
public static final ModelKey<SeaponyModel<?>> GUARDIAN = register("guardian", SeaponyModel::new);
public static final ModelKey<GuardianPonyModel> GUARDIAN = register("guardian", GuardianPonyModel::new);
public static final ModelKey<EnderStallionModel> ENDERMAN = register("enderman", EnderStallionModel::new);
public static final ModelKey<ParaspriteModel> VEX = register("vex", ParaspriteModel::new);
public static final ModelKey<SpikeModel> STRIDER = register("strider", SpikeModel::new);
public static final ModelKey<SaddleModel> STRIDER_SADDLE = register("strider_saddle", SaddleModel::new);
public static final ModelKey<BreezieModel> ALLAY = register("allay", BreezieModel::new);
public static final ModelKey<ParaspriteModel<VexEntity>> VEX = register("vex", ParaspriteModel::new);
public static final ModelKey<SpikeModel<StriderEntity>> STRIDER = register("strider", SpikeModel::new);
public static final ModelKey<SaddleModel<StriderEntity>> STRIDER_SADDLE = register("strider_saddle", SaddleModel::new);
public static final ModelKey<BreezieModel<AllayEntity>> ALLAY = register("allay", BreezieModel::new);
public static final ModelKey<PonyElytra<?>> ELYTRA = register("elytra", PonyElytra::new);
public static final ModelKey<PonyArmourStandModel> ARMOUR_STAND = register("armour_stand", PonyArmourStandModel::new);
public static final ModelKey<AbstractPonyModel<?>> INNER_VANILLA_ARMOR = register("armor/inner_vanilla_armor", PonyArmourModel::new);
public static final ModelKey<AbstractPonyModel<?>> OUTER_VANILLA_ARMOR = register("armor/outer_vanilla_armor", PonyArmourModel::new);
public static final ModelKey<AbstractPonyModel<?>> INNER_PONY_ARMOR = register("armor/inner_pony_armor", PonyArmourModel::new);
public static final ModelKey<AbstractPonyModel<?>> OUTER_PONY_ARMOR = register("armor/outer_pony_armor", PonyArmourModel::new);
public static final ModelKey<ArmorStandEntityModel> ARMOUR_STAND = register("armour_stand", PonyArmourStandModel::new);
public static final ModelKey<PonyArmourModel<?>> INNER_VANILLA_ARMOR = register("armor/inner_vanilla_armor", PonyArmourModel::new);
public static final ModelKey<PonyArmourModel<?>> OUTER_VANILLA_ARMOR = register("armor/outer_vanilla_armor", PonyArmourModel::new);
public static final ModelKey<PonyArmourModel<?>> INNER_PONY_ARMOR = register("armor/inner_pony_armor", PonyArmourModel::new);
public static final ModelKey<PonyArmourModel<?>> OUTER_PONY_ARMOR = register("armor/outer_pony_armor", PonyArmourModel::new);
public static final GearModelKey<AbstractGearModel> STETSON = registerGear("stetson", Wearable.STETSON, t -> new WearableGear(t, Wearable.STETSON, BodyPart.HEAD, 0.15F));
public static final GearModelKey<AbstractGearModel> STETSON = registerGear("stetson", Wearable.STETSON, t -> new WearableGear(Wearable.STETSON, BodyPart.HEAD, 0.15F));
public static final GearModelKey<SaddleBags> SADDLEBAGS_BOTH = registerGear("saddlebags", Wearable.SADDLE_BAGS_BOTH, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_BOTH));
public static final GearModelKey<SaddleBags> SADDLEBAGS_LEFT = registerGear(SADDLEBAGS_BOTH, Wearable.SADDLE_BAGS_LEFT, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_LEFT));
public static final GearModelKey<SaddleBags> SADDLEBAGS_RIGHT = registerGear(SADDLEBAGS_BOTH, Wearable.SADDLE_BAGS_RIGHT, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_RIGHT));
public static final GearModelKey<AbstractGearModel> CROWN = registerGear("crown", Wearable.CROWN, t -> new WearableGear(t.getChild("crown"), Wearable.CROWN, BodyPart.HEAD, 0.1F));
public static final GearModelKey<AbstractGearModel> MUFFIN = registerGear("muffin", Wearable.MUFFIN, t -> new WearableGear(t.getChild("crown"), Wearable.MUFFIN, BodyPart.HEAD, 0.45F));
public static final GearModelKey<AbstractGearModel> WITCH_HAT = registerGear("witch_hat", Wearable.HAT, t -> new WearableGear(t.getChild("hat"), Wearable.HAT, BodyPart.HEAD, 0.7F));
public static final GearModelKey<Crown> CROWN = registerGear("crown", Wearable.CROWN, Crown::new);
public static final GearModelKey<AbstractGearModel> MUFFIN = registerGear("muffin", Wearable.MUFFIN, t -> new WearableGear(Wearable.MUFFIN, BodyPart.HEAD, 0.45F).addPart(t.getChild("crown")));
public static final GearModelKey<AbstractGearModel> WITCH_HAT = registerGear("witch_hat", Wearable.HAT, t -> new WearableGear(Wearable.HAT, BodyPart.HEAD, 0.7F).addPart(t.getChild("hat")));
public static final GearModelKey<DeerAntlers> ANTLERS = registerGear("antlers", Wearable.ANTLERS, DeerAntlers::new);
public static final PlayerModelKey<AlicornModel<?>> ALICORN = registerPlayer("alicorn", Race.ALICORN, AlicornModel::new);
public static final PlayerModelKey<UnicornModel<?>> UNICORN = registerPlayer("unicorn", Race.UNICORN, UnicornModel::new);
public static final PlayerModelKey<KirinModel<?>> KIRIN = registerPlayer("kirin", Race.KIRIN, KirinModel::new);
public static final PlayerModelKey<PegasusModel<?>> PEGASUS = registerPlayer("pegasus", Race.PEGASUS, PegasusModel::new);
public static final PlayerModelKey<PegasusModel<?>> GRYPHON = registerPlayer("gryphon", Race.GRYPHON, PegasusModel::new);
public static final PlayerModelKey<PegasusModel<?>> HIPPOGRIFF = registerPlayer("hippogriff", Race.HIPPOGRIFF, PegasusModel::new, PonyArmourModel::new);
public static final PlayerModelKey<EarthPonyModel<?>> EARTH_PONY = registerPlayer("earth_pony", Race.EARTH, EarthPonyModel::new);
public static final PlayerModelKey<SeaponyModel<?>> SEA_PONY = registerPlayer("sea_pony", Race.SEAPONY, SeaponyModel::new, SeaponyModel.Armour::new);
public static final PlayerModelKey<PegasusModel<?>> BAT_PONY = registerPlayer("bat_pony", Race.BATPONY, PegasusModel::new);
public static final PlayerModelKey<ChangelingModel<?>> CHANGELING = registerPlayer("changeling", Race.CHANGELING, ChangelingModel::new);
public static final PlayerModelKey<ChangelingModel<?>> CHANGEDLING = registerPlayer("reformed_changeling", Race.CHANGEDLING, ChangelingModel::new);
public static final PlayerModelKey<EarthPonyModel<?>> ZEBRA = registerPlayer("zebra", Race.ZEBRA, EarthPonyModel::new);
public static final PlayerModelKey<LivingEntity, AlicornModel<?>> ALICORN = registerPlayer("alicorn", Race.ALICORN, AlicornModel::new);
public static final PlayerModelKey<LivingEntity, UnicornModel<?>> UNICORN = registerPlayer("unicorn", Race.UNICORN, UnicornModel::new);
public static final PlayerModelKey<LivingEntity, KirinModel<?>> KIRIN = registerPlayer("kirin", Race.KIRIN, KirinModel::new);
public static final PlayerModelKey<LivingEntity, PegasusModel<?>> PEGASUS = registerPlayer("pegasus", Race.PEGASUS, PegasusModel::new);
public static final PlayerModelKey<LivingEntity, PegasusModel<?>> GRYPHON = registerPlayer("gryphon", Race.GRYPHON, PegasusModel::new);
public static final PlayerModelKey<LivingEntity, PegasusModel<?>> HIPPOGRIFF = registerPlayer("hippogriff", Race.HIPPOGRIFF, PegasusModel::new, PonyArmourModel::new);
public static final PlayerModelKey<LivingEntity, EarthPonyModel<?>> EARTH_PONY = registerPlayer("earth_pony", Race.EARTH, EarthPonyModel::new);
public static final PlayerModelKey<LivingEntity, SeaponyModel<?>> SEA_PONY = registerPlayer("sea_pony", Race.SEAPONY, SeaponyModel::new, SeaponyModel.Armour::new);
public static final PlayerModelKey<LivingEntity, PegasusModel<?>> BAT_PONY = registerPlayer("bat_pony", Race.BATPONY, PegasusModel::new);
public static final PlayerModelKey<LivingEntity, ChangelingModel<?>> CHANGELING = registerPlayer("changeling", Race.CHANGELING, ChangelingModel::new);
public static final PlayerModelKey<LivingEntity, ChangelingModel<?>> CHANGEDLING = registerPlayer("reformed_changeling", Race.CHANGEDLING, ChangelingModel::new);
public static final PlayerModelKey<LivingEntity, EarthPonyModel<?>> ZEBRA = registerPlayer("zebra", Race.ZEBRA, EarthPonyModel::new);
static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<T> registerPlayer(String name, Race race,
static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<E, T> registerPlayer(String name, Race race,
BiFunction<ModelPart, Boolean, T> constructor) {
return registerPlayer(name, race, constructor, PonyArmourModel::new);
}
@SuppressWarnings("unchecked")
static <T extends Model & PonyModel<?>> PlayerModelKey<T> registerPlayer(String name, Race race,
static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<E, T> registerPlayer(String name, Race race,
BiFunction<ModelPart, Boolean, T> constructor,
MsonModel.Factory<AbstractPonyModel<?>> armorFactory) {
return (PlayerModelKey<T>)PLAYER_MODELS.computeIfAbsent(race, r -> new PlayerModelKey<T>(name, constructor, armorFactory));
MsonModel.Factory<PonyArmourModel<E>> armorFactory) {
return (PlayerModelKey<E, T>)PLAYER_MODELS.computeIfAbsent(race, r -> new PlayerModelKey<>(name, constructor, armorFactory));
}
@SuppressWarnings("unchecked")
static <T extends AbstractGearModel> GearModelKey<T> registerGear(String name, Wearable wearable, MsonModel.Factory<T> constructor) {
return (GearModelKey<T>)GEAR_MODELS.computeIfAbsent(wearable, w -> {
return new GearModelKey<T>(Mson.getInstance().registerModel(MineLittlePony.id("gear/" + name), constructor), constructor);
return new GearModelKey<T>(Mson.getInstance().registerModel(new Identifier("minelittlepony", "gear/" + name), constructor), constructor);
});
}
@ -103,13 +103,13 @@ public final class ModelType {
}
static <T extends Model> ModelKey<T> register(String name, MsonModel.Factory<T> constructor) {
return new ModelKeyImpl<T>(MineLittlePony.id(name), constructor);
return new ModelKeyImpl<T>(new Identifier("minelittlepony", name), constructor);
}
@SuppressWarnings("unchecked")
@Nullable
public static <T extends Model & PonyModel<?>> PlayerModelKey<T> getPlayerModel(Race race) {
return (PlayerModelKey<T>)PLAYER_MODELS.get(race);
public static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<E, T> getPlayerModel(Race race) {
return (PlayerModelKey<E, T>)PLAYER_MODELS.get(race);
}
public static Stream<Map.Entry<Wearable, GearModelKey<? extends Gear>>> getWearables() {

View file

@ -2,28 +2,27 @@ package com.minelittlepony.client.model;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.ModelPart;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.model.Models;
import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.mson.api.*;
import java.util.function.*;
public record PlayerModelKey<M extends Model & PonyModel<?>> (
public record PlayerModelKey<T extends LivingEntity, M extends Model & PonyModel<?>> (
ModelKey<M> steveKey,
ModelKey<M> alexKey,
MsonModel.Factory<AbstractPonyModel<?>> armorFactory
MsonModel.Factory<PonyArmourModel<T>> armorFactory
) {
PlayerModelKey(String name,
BiFunction<ModelPart, Boolean, M> modelFactory,
MsonModel.Factory<AbstractPonyModel<?>> armorFactory
) {
PlayerModelKey(String name, BiFunction<ModelPart, Boolean, M> modelFactory, MsonModel.Factory<PonyArmourModel<T>> armorFactory) {
this(
new ModelKeyImpl<>(MineLittlePony.id("races/steve/" + name), tree -> modelFactory.apply(tree, false)),
new ModelKeyImpl<>(MineLittlePony.id("races/alex/" + name), tree -> modelFactory.apply(tree, true)),
new ModelKeyImpl<>(new Identifier("minelittlepony", "races/steve/" + name), tree -> modelFactory.apply(tree, false)),
new ModelKeyImpl<>(new Identifier("minelittlepony", "races/alex/" + name), tree -> modelFactory.apply(tree, true)),
armorFactory
);
}
@ -32,11 +31,12 @@ public record PlayerModelKey<M extends Model & PonyModel<?>> (
return slimArms ? alexKey : steveKey;
}
public <N extends M> Models<N> create(boolean slimArms) {
public <E extends T, N extends M> Models<E, N> create(boolean slimArms) {
return create(slimArms, null);
}
public <N extends M> Models<N> create(boolean slimArms, @Nullable Consumer<N> initializer) {
return new Models<>(this, slimArms, initializer);
@SuppressWarnings({"rawtypes", "unchecked"})
public <E extends T, N extends M> Models<E, N> create(boolean slimArms, @Nullable Consumer<N> initializer) {
return new Models(this, slimArms, initializer);
}
}

View file

@ -1,30 +1,90 @@
package com.minelittlepony.client.model;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.render.entity.state.BipedEntityRenderState;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.entity.model.AnimalModel;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import com.google.common.collect.ImmutableList;
/**
* Modified from ModelElytra.
*/
public class PonyElytra<T extends BipedEntityRenderState> extends EntityModel<T> {
public class PonyElytra<T extends LivingEntity> extends AnimalModel<T> {
public boolean isSneaking;
private final ModelPart rightWing;
private final ModelPart leftWing;
public PonyElytra(ModelPart root) {
super(root);
rightWing = root.getChild("right_wing");
leftWing = root.getChild("left_wing");
public PonyElytra(ModelPart tree) {
rightWing = tree.getChild("right_wing");
leftWing = tree.getChild("left_wing");
}
@Override
public void setAngles(T state) {
leftWing.pitch = state.leftWingPitch;
leftWing.yaw = state.leftWingYaw;
leftWing.roll = state.leftWingRoll;
protected Iterable<ModelPart> getHeadParts() {
return ImmutableList.of();
}
@Override
protected Iterable<ModelPart> getBodyParts() {
return ImmutableList.of(leftWing, rightWing);
}
/**
* Sets the model's various rotation angles.
*
* See {@link AbstractPonyModel.setRotationAngles} for an explanation of the various parameters.
*/
@Override
public void setAngles(T entity, float limbDistance, float limbAngle, float age, float headYaw, float headPitch) {
float rotateX = MathHelper.PI / 2;
float rotateY = MathHelper.PI / 8;
float rotateZ = MathHelper.PI / 12;
float rpY = 0;
if (entity.isFallFlying()) {
float velY = 1;
Vec3d motion = entity.getVelocity();
if (motion.y < 0) {
velY = 1 - (float) Math.pow(-motion.normalize().y, 1.5);
}
rotateX = velY * MathHelper.PI * (2 / 3F) + (1 - velY) * rotateX;
rotateY = velY * (MathHelper.PI / 2) + (1 - velY) * rotateY;
} else if (isSneaking) {
rotateX = MathHelper.PI * 1.175F;
rotateY = MathHelper.PI / 2;
rotateZ = MathHelper.PI / 4;
rpY = AbstractPonyModel.BODY_SNEAKING.y();
}
leftWing.pivotX = 5;
leftWing.pivotY = rpY;
if (entity instanceof AbstractClientPlayerEntity) {
AbstractClientPlayerEntity player = (AbstractClientPlayerEntity) entity;
player.elytraPitch += (rotateX - player.elytraPitch) / 10;
player.elytraYaw += (rotateY - player.elytraYaw) / 10;
player.elytraRoll += (rotateZ - player.elytraRoll) / 10;
leftWing.pitch = player.elytraPitch;
leftWing.yaw = player.elytraYaw;
leftWing.roll = player.elytraRoll;
} else {
leftWing.pitch = rotateX;
leftWing.yaw = rotateZ;
leftWing.roll = rotateY;
}
rightWing.pivotX = -leftWing.pivotX;
rightWing.pivotY = leftWing.pivotY;
rightWing.pitch = leftWing.pitch;
rightWing.yaw = -leftWing.yaw;
rightWing.roll = -leftWing.roll;

View file

@ -1,25 +1,23 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.item.Item;
import net.minecraft.client.render.entity.equipment.EquipmentModel;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;
import com.minelittlepony.client.model.AbstractPonyModel;
import com.minelittlepony.mson.api.ModelKey;
import com.minelittlepony.mson.api.Mson;
import java.util.*;
public interface ArmorModelRegistry {
static final Map<Identifier, Optional<ModelKey<AbstractPonyModel<?>>>> REGISTRY = new HashMap<>();
static final Map<Identifier, Optional<ModelKey<PonyArmourModel<?>>>> REGISTRY = new HashMap<>();
public static Optional<ModelKey<AbstractPonyModel<?>>> getModelKey(Item item, EquipmentModel.LayerType layerType) {
public static Optional<ModelKey<PonyArmourModel<?>>> getModelKey(Item item, ArmourLayer layer) {
Identifier id = Registries.ITEM.getId(item);
if (id.getNamespace().equals("minecraft")) {
return Optional.empty();
}
return REGISTRY.computeIfAbsent(id.withPath(p -> "armor/" + layerType.name().toLowerCase(Locale.ROOT) + "_" + p + ".json"), i -> {
return REGISTRY.computeIfAbsent(id.withPath(p -> "armor/" + layer.name().toLowerCase(Locale.ROOT) + "_" + p + ".json"), i -> {
return Optional.of(Mson.getInstance().registerModel(i, PonyArmourModel::new));
}).filter(key -> key.getModelData().isPresent());
}

View file

@ -0,0 +1,23 @@
package com.minelittlepony.client.model.armour;
/**
* The layer used to render a given armour piece.
*/
public enum ArmourLayer {
/**
* Hanging loose and sagging free
*/
OUTER,
/**
* Fits snugly to the player's model.
*/
INNER;
public int getLegacyId() {
return ordinal() + 1;
}
public boolean isInner() {
return this == INNER;
}
}

View file

@ -1,159 +0,0 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.model.Model;
import net.minecraft.client.render.*;
import net.minecraft.client.render.entity.equipment.EquipmentModel;
import net.minecraft.client.render.entity.state.BipedEntityRenderState;
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.component.type.DyedColorComponent;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.*;
import net.minecraft.item.equipment.EquipmentAsset;
import net.minecraft.item.equipment.trim.ArmorTrim;
import net.minecraft.item.equipment.trim.ArmorTrimMaterial;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.registry.tag.ItemTags;
import net.minecraft.util.*;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public interface ArmourRendererPlugin {
AtomicReference<ArmourRendererPlugin> INSTANCE = new AtomicReference<>(new ArmourRendererPlugin() {});
static void register(Function<ArmourRendererPlugin, ArmourRendererPlugin> constructor) {
INSTANCE.set(constructor.apply(INSTANCE.get()));
}
default ArmourTextureLookup getTextureLookup() {
return ArmourTextureResolver.INSTANCE;
}
default void onArmourRendered(LivingEntityRenderState state, MatrixStack matrices, VertexConsumerProvider provider, EquipmentSlot armorSlot, EquipmentModel.LayerType layerType, ArmourType type) {
}
default ItemStack[] getArmorStacks(BipedEntityRenderState state, EquipmentSlot armorSlot, EquipmentModel.LayerType layerType, ArmourType type) {
return new ItemStack[] { switch (armorSlot) {
case HEAD -> state.equippedHeadStack;
case CHEST -> state.equippedChestStack;
case LEGS -> state.equippedLegsStack;
case FEET -> state.equippedFeetStack;
case BODY -> state.equippedChestStack;
default -> ItemStack.EMPTY;
// TODO: Mojaaaaaaang!!
//case MAINHAND -> state.getMainHandStack();
//case OFFHAND -> state.mainArm == Arm.LEFT ? state.leftHandStack : state.rightHandStack;
}};
}
default float getGlintAlpha(EquipmentSlot slot, ItemStack stack) {
return stack.hasGlint() ? 1 : 0;
}
default int getDyeColor(EquipmentSlot slot, ItemStack stack) {
return stack.isIn(ItemTags.DYEABLE) ? DyedColorComponent.getColor(stack, -6265536) : Colors.WHITE;
}
default float getArmourAlpha(EquipmentSlot slot, EquipmentModel.LayerType layer) {
return 1F;
}
default float getTrimAlpha(EquipmentSlot slot, ArmorTrim trim, EquipmentModel.LayerType layer) {
return 1F;
}
default float getElytraAlpha(ItemStack stack, Model model, LivingEntityRenderState entity) {
return stack.isOf(Items.ELYTRA) ? 1F : 0F;
}
@Nullable
default VertexConsumer getTrimConsumer(EquipmentSlot slot, VertexConsumerProvider provider, ArmorTrim trim, EquipmentModel.LayerType layerType, RegistryKey<EquipmentAsset> assetId) {
@Nullable VertexConsumer buffer = getOptionalBuffer(provider, getTrimLayer(slot, trim, layerType, assetId));
if (buffer == null) {
return null;
}
SpriteAtlasTexture armorTrimsAtlas = MinecraftClient.getInstance().getBakedModelManager().getAtlas(TexturedRenderLayers.ARMOR_TRIMS_ATLAS_TEXTURE);
Sprite sprite = armorTrimsAtlas.getSprite(getTexture(trim, layerType, assetId));
return sprite.getTextureSpecificVertexConsumer(buffer);
}
private static String getAssetName(RegistryEntry<ArmorTrimMaterial> material, RegistryKey<EquipmentAsset> assetKey) {
String string = (String)material.value().overrideArmorAssets().get(assetKey);
return string != null ? string : material.value().assetName();
}
private static Identifier getTexture(ArmorTrim trim, EquipmentModel.LayerType layerType, RegistryKey<EquipmentAsset> assetId) {
Identifier identifier = trim.pattern().value().assetId();
String string = getAssetName(trim.material(), assetId);
return identifier.withPath((UnaryOperator<String>)(path -> "trims/entity/" + layerType.asString() + "/" + path + "_" + string));
}
@Nullable
default RenderLayer getTrimLayer(EquipmentSlot slot, ArmorTrim trim, EquipmentModel.LayerType layerType, RegistryKey<EquipmentAsset> assetId) {
return TexturedRenderLayers.getArmorTrims(trim.pattern().value().decal());
}
@Nullable
default VertexConsumer getArmourConsumer(EquipmentSlot slot, VertexConsumerProvider provider, Identifier texture, EquipmentModel.LayerType layer) {
return getOptionalBuffer(provider, getArmourLayer(slot, texture, layer));
}
@Nullable
default RenderLayer getArmourLayer(EquipmentSlot slot, Identifier texture, EquipmentModel.LayerType layer) {
return RenderLayer.getArmorCutoutNoCull(texture);
}
@Nullable
default VertexConsumer getGlintConsumer(EquipmentSlot slot, VertexConsumerProvider provider, EquipmentModel.LayerType layer) {
return getOptionalBuffer(provider, getGlintLayer(slot, layer));
}
@Nullable
default RenderLayer getGlintLayer(EquipmentSlot slot, EquipmentModel.LayerType layer) {
return RenderLayer.getArmorEntityGlint();
}
@Nullable
default VertexConsumer getCapeConsumer(BipedEntityRenderState entity, VertexConsumerProvider provider, Identifier texture) {
if (entity.equippedChestStack.isOf(Items.ELYTRA)) {
return null;
}
return getOptionalBuffer(provider, getCapeLayer(entity, texture));
}
@Nullable
default RenderLayer getCapeLayer(BipedEntityRenderState entity, Identifier texture) {
return RenderLayer.getEntitySolid(texture);
}
/**
* @deprecated Method is no longer used
*/
@Deprecated
@Nullable
default VertexConsumer getElytraConsumer(ItemStack stack, Model model, BipedEntityRenderState state, VertexConsumerProvider provider, Identifier texture) {
return ItemRenderer.getArmorGlintConsumer(provider, RenderLayer.getArmorCutoutNoCull(texture), getGlintAlpha(EquipmentSlot.CHEST, stack) > 0F);
}
@Nullable
static VertexConsumer getOptionalBuffer(VertexConsumerProvider provider, @Nullable RenderLayer layer) {
return layer == null ? null : provider.getBuffer(layer);
}
public enum ArmourType {
ARMOUR,
CAPE,
ELYTRA,
SKULL
}
}

View file

@ -1,35 +0,0 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.client.texture.TextureManager;
import net.minecraft.util.Identifier;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.util.ResourceUtil;
import java.util.stream.Stream;
public record ArmourTexture(Identifier texture, ArmourVariant variant) {
private static final Interner<ArmourTexture> INTERNER = Interners.newWeakInterner();
public static final ArmourTexture UNKNOWN = legacy(TextureManager.MISSING_IDENTIFIER);
public boolean validate() {
return texture != TextureManager.MISSING_IDENTIFIER && ResourceUtil.textureExists(texture);
}
public static ArmourTexture legacy(Identifier texture) {
return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.LEGACY));
}
public static ArmourTexture modern(Identifier texture) {
return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.NORMAL));
}
public Stream<ArmourTexture> ponify() {
if (!PonyConfig.getInstance().disablePonifiedArmour.get()) {
return Stream.of(modern(texture().withPath(p -> p.replace("humanoid", "ponified"))), this);
}
return Stream.of(this);
}
}

View file

@ -1,8 +0,0 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.client.render.entity.equipment.EquipmentModel;
import net.minecraft.item.ItemStack;
public interface ArmourTextureLookup {
ArmourTexture getTexture(ItemStack stack, EquipmentModel.LayerType layerType, EquipmentModel.Layer layer);
}

View file

@ -1,19 +1,24 @@
package com.minelittlepony.client.model.armour;
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.minecraft.client.render.entity.equipment.EquipmentModel;
import net.minecraft.item.*;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceReloader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.TextureManager;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtElement;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.util.ResourceUtil;
import com.google.common.cache.*;
import com.minelittlepony.client.MineLittlePony;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* The default texture resolver used by Mine Little Pony.
@ -30,51 +35,100 @@ import java.util.stream.Stream;
* - the "minecraft" namespace is always replaced with "minelittlepony"
* <p>
*/
public class ArmourTextureResolver implements ArmourTextureLookup, IdentifiableResourceReloadListener {
public static final Identifier ID = MineLittlePony.id("armor_textures");
public class ArmourTextureResolver {
public static final ArmourTextureResolver INSTANCE = new ArmourTextureResolver();
private final LoadingCache<ArmourParameters, ArmourTexture> layerCache = CacheBuilder.newBuilder()
private final Cache<String, Identifier> cache = CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.SECONDS)
.build(CacheLoader.from(parameters -> Stream.of(ArmourTexture.legacy(parameters.textureId())).flatMap(this::performLookup).findFirst().orElse(ArmourTexture.UNKNOWN)));
private Stream<ArmourTexture> performLookup(ArmourTexture id) {
List<ArmourTexture> options = Stream.of(id).flatMap(ArmourTexture::ponify).toList();
return options.stream().distinct()
.filter(ArmourTexture::validate)
.findFirst()
.or(() -> {
MineLittlePony.LOGGER.warn("Could not identify correct texture to use for {}. Was none of: [" + System.lineSeparator() + "{}" + System.lineSeparator() + "]", id, options.stream()
.map(ArmourTexture::texture)
.map(Identifier::toString)
.collect(Collectors.joining("," + System.lineSeparator())));
return Optional.empty();
}).stream();
}
.<String, Identifier>build();
public void invalidate() {
layerCache.invalidateAll();
cache.invalidateAll();
}
@Override
public CompletableFuture<Void> reload(ResourceReloader.Synchronizer synchronizer, ResourceManager manager, Executor prepareExecutor, Executor applyExecutor) {
return CompletableFuture.runAsync(this::invalidate, prepareExecutor).thenCompose(synchronizer::whenPrepared);
}
public Identifier getTexture(LivingEntity entity, ItemStack stack, EquipmentSlot slot, ArmourLayer layer, @Nullable String type) {
Identifier material = stack.getItem() instanceof ArmorItem armor
? new Identifier(armor.getMaterial().getName())
: Registries.ITEM.getId(stack.getItem());
String custom = getCustom(stack);
@Override
public Identifier getFabricId() {
return ID;
}
try {
return cache.get(String.format("%s#%s#%s#%s", material, layer, type, custom), () -> {
String typed = Strings.nullToEmpty(type);
String extra = typed.isEmpty() ? "" : "_" + typed;
@Override
public ArmourTexture getTexture(ItemStack stack, EquipmentModel.LayerType layerType, EquipmentModel.Layer layer) {
layerCache.invalidateAll();
return layerCache.getUnchecked(new ArmourParameters(layer, layerType));
}
Identifier texture;
private record ArmourParameters(EquipmentModel.Layer layer, EquipmentModel.LayerType layerType) {
public Identifier textureId() {
return layer.getFullTextureId(layerType);
if (!"none".equals(custom)) {
texture = resolveNewOrOld(material, layer, custom + extra, typed);
if (texture != null) {
return texture;
}
}
texture = resolveNewOrOld(material, layer, extra, typed);
if (texture != null) {
return texture;
}
return TextureManager.MISSING_IDENTIFIER;
});
} catch (ExecutionException ignored) {
return TextureManager.MISSING_IDENTIFIER;
}
}
private String getCustom(ItemStack stack) {
if (stack.hasNbt() && stack.getNbt().contains("CustomModelData", NbtElement.NUMBER_TYPE)) {
return "custom_" + stack.getNbt().getInt("CustomModelData");
}
return "none";
}
@Nullable
private Identifier resolveNewOrOld(Identifier material, ArmourLayer layer, String extra, String type) {
Identifier texture = resolveHumanOrPony(ResourceUtil.format("%s:textures/models/armor/%s_layer_%s%s.png", material.getNamespace(), material.getPath(), layer, extra), type);
if (texture != null) {
return texture;
}
return resolveHumanOrPony(ResourceUtil.format("%s:textures/models/armor/%s_layer_%d%s.png", material.getNamespace(), material.getPath(), layer.getLegacyId(), extra), type);
}
@Nullable
private Identifier resolveHumanOrPony(String res, String type) {
Identifier human = new Identifier(res);
String domain = human.getNamespace();
if ("minecraft".equals(domain)) {
domain = "minelittlepony"; // it's a vanilla armor. I provide these.
}
if (!PonyConfig.getInstance().disablePonifiedArmour.get()) {
Identifier pony = new Identifier(domain, human.getPath().replace(".png", "_pony.png"));
if (isValid(pony)) {
return pony;
}
}
if (isValid(human)) {
return human;
}
return null;
}
private final boolean isValid(Identifier texture) {
return MinecraftClient.getInstance().getResourceManager().getResource(texture).isPresent();
}
public ArmourVariant getVariant(ArmourLayer layer, Identifier resolvedTexture) {
if (resolvedTexture.getPath().endsWith("_pony.png")) {
return ArmourVariant.NORMAL;
}
return ArmourVariant.LEGACY;
}
}

View file

@ -1,8 +1,5 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.client.render.entity.equipment.EquipmentModel;
import com.minelittlepony.client.model.AbstractPonyModel;
import com.minelittlepony.client.model.ModelType;
import com.minelittlepony.mson.api.ModelKey;
@ -13,15 +10,15 @@ public enum ArmourVariant {
LEGACY(ModelType.INNER_VANILLA_ARMOR, ModelType.OUTER_VANILLA_ARMOR),
TRIM(ModelType.INNER_VANILLA_ARMOR, ModelType.OUTER_VANILLA_ARMOR);
private final Optional<ModelKey<AbstractPonyModel<?>>> innerModel;
private final Optional<ModelKey<AbstractPonyModel<?>>> outerModel;
private final Optional<ModelKey<PonyArmourModel<?>>> innerModel;
private final Optional<ModelKey<PonyArmourModel<?>>> outerModel;
ArmourVariant(ModelKey<AbstractPonyModel<?>> inner, ModelKey<AbstractPonyModel<?>> outer) {
ArmourVariant(ModelKey<PonyArmourModel<?>> inner, ModelKey<PonyArmourModel<?>> outer) {
this.innerModel = Optional.of(inner);
this.outerModel = Optional.of(outer);
}
public Optional<ModelKey<AbstractPonyModel<?>>> getDefaultModel(EquipmentModel.LayerType layerType) {
return layerType == EquipmentModel.LayerType.HUMANOID_LEGGINGS ? innerModel : outerModel;
public Optional<ModelKey<PonyArmourModel<?>>> getDefaultModel(ArmourLayer layer) {
return layer.isInner() ? innerModel : outerModel;
}
}

View file

@ -1,154 +0,0 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.*;
import net.minecraft.client.render.entity.equipment.*;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.DyedColorComponent;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.item.equipment.EquipmentAsset;
import net.minecraft.item.equipment.trim.ArmorTrim;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.tag.ItemTags;
import net.minecraft.util.Colors;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.ColorHelper;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.model.Models;
import com.minelittlepony.client.model.AbstractPonyModel;
import com.minelittlepony.client.model.ClientPonyModel;
import com.minelittlepony.client.render.entity.state.PonyRenderState;
import java.util.*;
public class PonifiedEquipmentRenderer extends EquipmentRenderer {
private static final int TRANSPARENT = 0;
private final EquipmentModelLoader modelLoader;
private @Nullable Set<EntityModel<?>> drawnModels;
public PonifiedEquipmentRenderer(EquipmentModelLoader modelLoader) {
super(modelLoader, MinecraftClient.getInstance().getBakedModelManager().getAtlas(TexturedRenderLayers.ARMOR_TRIMS_ATLAS_TEXTURE));
this.modelLoader = modelLoader;
}
public <S extends PonyRenderState, V extends ClientPonyModel<S>> void render(
EquipmentSlot equipmentSlot,
EquipmentModel.LayerType layerType,
RegistryKey<EquipmentAsset> assetId,
S entity,
Models<V> models,
ItemStack stack,
MatrixStack matrices,
VertexConsumerProvider vertexConsumers,
int light
) {
render(equipmentSlot, layerType, assetId, entity, models, stack, matrices, vertexConsumers, light, null);
}
public <S extends PonyRenderState, V extends ClientPonyModel<S>> void render(
EquipmentSlot equipmentSlot,
EquipmentModel.LayerType layerType,
RegistryKey<EquipmentAsset> assetId,
S entity,
Models<V> models,
ItemStack stack,
MatrixStack matrices,
VertexConsumerProvider vertices,
int light,
@Nullable Identifier texture
) {
List<EquipmentModel.Layer> layers = modelLoader.get(assetId).getLayers(layerType);
if (!layers.isEmpty()) {
ArmourRendererPlugin plugin = ArmourRendererPlugin.INSTANCE.get();
int defaultColor = stack.isIn(ItemTags.DYEABLE) ? DyedColorComponent.getColor(stack, 0) : 0;
float armorAlpha = plugin.getArmourAlpha(equipmentSlot, layerType);
boolean hasGlint = plugin.getGlintAlpha(equipmentSlot, stack) > 0;
if (armorAlpha > 0) {
for (EquipmentModel.Layer layer : layers) {
int dyeColor = getDyeColor(layer, defaultColor);
if (dyeColor != TRANSPARENT) {
ArmourTexture armorTexture = plugin.getTextureLookup().getTexture(stack, layerType, layer);
Identifier layerTexture = layer.usePlayerTexture() && texture != null ? texture : armorTexture.texture();
@Nullable
VertexConsumer armorConsumer = getArmorVertexConsumer(plugin, equipmentSlot, vertices, layerTexture, layerType, hasGlint);
if (armorConsumer != null) {
ArmourVariant variant = layer.usePlayerTexture() ? ArmourVariant.LEGACY : armorTexture.variant();
AbstractPonyModel<?> model = models.getArmourModel(stack, layerType, variant).orElse(null);
if (model != null) {
model.setAngles(entity);
models.body().copyTransforms(model);
if (setVisibilities(model, equipmentSlot, layerType)) {
model.render(matrices, armorConsumer, light, OverlayTexture.DEFAULT_UV, dyeColor);
if (drawnModels == null) {
drawnModels = new HashSet<>();
}
drawnModels.add(model);
}
}
}
}
}
}
if (drawnModels != null) {
@Nullable
ArmorTrim armorTrim = stack.get(DataComponentTypes.TRIM);
@Nullable
VertexConsumer trimConsumer = armorTrim != null && plugin.getTrimAlpha(equipmentSlot, armorTrim, layerType) > 0 ? plugin.getTrimConsumer(equipmentSlot, vertices, armorTrim, layerType, assetId) : null;
if (trimConsumer != null) {
for (EntityModel<?> model : drawnModels) {
model.render(matrices, trimConsumer, light, OverlayTexture.DEFAULT_UV);
}
}
}
drawnModels = null;
}
}
@Nullable
private static VertexConsumer getArmorVertexConsumer(ArmourRendererPlugin plugin, EquipmentSlot slot, VertexConsumerProvider provider, Identifier texture, EquipmentModel.LayerType layerType, boolean glint) {
VertexConsumer armorConsumer = plugin.getArmourConsumer(slot, provider, texture, layerType);
if (armorConsumer != null) {
VertexConsumer glintConsumer = glint ? plugin.getGlintConsumer(slot, provider, layerType) : null;
if (glintConsumer != null) {
return VertexConsumers.union(glintConsumer, armorConsumer);
}
}
return armorConsumer;
}
private static int getDyeColor(EquipmentModel.Layer layer, int dyeColor) {
Optional<EquipmentModel.Dyeable> optional = layer.dyeable();
if (optional.isPresent()) {
int i = optional.get().colorWhenUndyed().map(ColorHelper::fullAlpha).orElse(0);
return dyeColor != TRANSPARENT ? dyeColor : i;
}
return Colors.WHITE;
}
public static boolean setVisibilities(AbstractPonyModel<?> model, EquipmentSlot slot, EquipmentModel.LayerType layer) {
model.setVisible(false);
model.body.visible = slot == EquipmentSlot.CHEST;
model.head.visible = layer == EquipmentModel.LayerType.HUMANOID && slot == EquipmentSlot.HEAD;
if (slot == (layer == EquipmentModel.LayerType.HUMANOID ? EquipmentSlot.FEET : EquipmentSlot.LEGS)) {
model.rightArm.visible = true;
model.leftArm.visible = true;
model.rightLeg.visible = true;
model.leftLeg.visible = true;
return true;
}
return model.head.visible || model.body.visible;
}
}

View file

@ -1,13 +1,52 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.client.model.AbstractPonyModel;
import com.minelittlepony.client.render.entity.state.PonyRenderState;
public class PonyArmourModel<T extends PonyRenderState> extends AbstractPonyModel<T> {
public class PonyArmourModel<T extends LivingEntity> extends AbstractPonyModel<T> {
public PonyArmourModel(ModelPart tree) {
super(tree, false);
super(tree);
}
public boolean poseModel(T entity, float limbAngle, float limbDistance, float age, float headYaw, float headPitch,
EquipmentSlot slot, ArmourLayer layer,
PonyModel<T> mainModel) {
if (!setVisibilities(slot, layer)) {
return false;
}
mainModel.copyAttributes(this);
setAngles(entity, limbAngle, limbDistance, age, headYaw, headPitch);
if (mainModel instanceof BipedEntityModel<?> biped) {
head.copyTransform(biped.head);
body.copyTransform(biped.body);
rightArm.copyTransform(biped.rightArm);
leftArm.copyTransform(biped.leftArm);
rightLeg.copyTransform(biped.rightLeg);
leftLeg.copyTransform(biped.leftLeg);
}
return true;
}
public boolean setVisibilities(EquipmentSlot slot, ArmourLayer layer) {
setVisible(false);
body.visible = slot == EquipmentSlot.CHEST;
head.visible = layer == ArmourLayer.OUTER && slot == EquipmentSlot.HEAD;
if (slot == (layer == ArmourLayer.OUTER ? EquipmentSlot.FEET : EquipmentSlot.LEGS)) {
rightArm.visible = true;
leftArm.visible = true;
rightLeg.visible = true;
leftLeg.visible = true;
return true;
}
return head.visible || body.visible;
}
}

View file

@ -2,19 +2,24 @@ package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.entity.EntityPose;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm;
import net.minecraft.util.Hand;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.client.render.entity.AllayRenderer;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
public class BreezieModel extends BipedEntityModel<AllayRenderer.State> {
public class BreezieModel<T extends LivingEntity> extends BipedEntityModel<T> {
private ModelPart neck;
private ModelPart leftWing;
private ModelPart rightWing;
public BreezieModel(ModelPart tree) {
super(tree);
neck = tree.getChild("neck");
leftWing = tree.getChild("left_wing");
rightWing = tree.getChild("right_wing");
}
@ -26,13 +31,15 @@ public class BreezieModel extends BipedEntityModel<AllayRenderer.State> {
}
@Override
public void setAngles(AllayRenderer.State state) {
protected Iterable<ModelPart> getBodyParts() {
return Iterables.concat(super.getBodyParts(), ImmutableList.of(neck, leftWing, rightWing));
}
float move = state.limbFrequency;
float swing = state.limbAmplitudeMultiplier;
@Override
public void setAngles(T entity, float move, float swing, float ticks, float headYaw, float headPitch) {
head.yaw = state.yawDegrees * 0.017453292F;
head.pitch = state.pitch * 0.017453292F;
head.yaw = headYaw * 0.017453292F;
head.pitch = headPitch * 0.017453292F;
hat.copyTransform(head);
@ -43,7 +50,7 @@ public class BreezieModel extends BipedEntityModel<AllayRenderer.State> {
leftLeg .setAngles(swing * MathHelper.cos(move * 0.6662F + MathHelper.PI) * 1.4F, 0, 0);
rightLeg.setAngles(swing * MathHelper.cos(move * 0.6662F) * 1.4F, 0, 0);
if (state.isInPose(EntityPose.SITTING)) {
if (riding) {
leftArm.pitch += -MathHelper.PI / 5;
rightArm.pitch += -MathHelper.PI / 5;
@ -51,15 +58,15 @@ public class BreezieModel extends BipedEntityModel<AllayRenderer.State> {
rotateLegRiding(rightLeg, 1);
}
rotateArm(leftArm, state.leftArmPose, 1);
rotateArm(rightArm, state.rightArmPose, 1);
rotateArm(leftArm, leftArmPose, 1);
rotateArm(rightArm, rightArmPose, 1);
if (state.handSwingProgress > 0) {
swingArms(state, state.preferredArm);
if (handSwingProgress > 0) {
swingArms(getPreferredArm(entity));
}
float rotX = MathHelper.sin(state.age * 0.067F) * 0.05F;
float rotZ = MathHelper.cos(state.age * 0.09F) * 0.05F + 0.05F;
float rotX = MathHelper.sin(ticks * 0.067F) * 0.05F;
float rotZ = MathHelper.cos(ticks * 0.09F) * 0.05F + 0.05F;
leftArm.pitch -= rotX;
leftArm.roll -= rotZ;
@ -67,8 +74,8 @@ public class BreezieModel extends BipedEntityModel<AllayRenderer.State> {
rightArm.pitch += rotX;
rightArm.roll += rotZ;
rotX = MathHelper.sin(state.age * 0.3F) * 0.05F;
rotZ = MathHelper.cos(state.age * 0.2F) * 0.05F + 0.05F;
rotX = MathHelper.sin(ticks * 0.3F) * 0.05F;
rotZ = MathHelper.cos(ticks * 0.2F) * 0.05F + 0.05F;
rotX -= 0.05F;
@ -77,19 +84,25 @@ public class BreezieModel extends BipedEntityModel<AllayRenderer.State> {
rightWing.yaw = -rotX * 10;
rightWing.pitch = rotZ;
if (state.rightArmPose == ArmPose.BOW_AND_ARROW) {
if (rightArmPose == ArmPose.BOW_AND_ARROW) {
raiseArm(rightArm, leftArm, -1);
} else if (state.leftArmPose == ArmPose.BOW_AND_ARROW) {
} else if (leftArmPose == ArmPose.BOW_AND_ARROW) {
raiseArm(leftArm, rightArm, 1);
}
}
private Arm getPreferredArm(T livingEntity) {
Arm arm = livingEntity.getMainArm();
return livingEntity.preferredHand == Hand.MAIN_HAND ? arm : arm.getOpposite();
}
protected void rotateLegRiding(ModelPart leg, float factor) {
leg.setAngles(-1.4137167F, factor * MathHelper.PI / 10, factor * 0.07853982F);
}
protected void swingArms(AllayRenderer.State state, Arm mainHand) {
body.yaw = MathHelper.sin(MathHelper.sqrt(state.handSwingProgress) * MathHelper.TAU) / 5;
protected void swingArms(Arm mainHand) {
body.yaw = MathHelper.sin(MathHelper.sqrt(handSwingProgress) * MathHelper.TAU) / 5;
if (mainHand == Arm.LEFT) {
body.yaw *= -1;
@ -107,15 +120,15 @@ public class BreezieModel extends BipedEntityModel<AllayRenderer.State> {
rightArm.pivotX = -cos;
rightArm.pivotZ = sin;
float swingAmount = 1 - (float)Math.pow(1 - state.handSwingProgress, 4);
float swingAmount = 1 - (float)Math.pow(1 - handSwingProgress, 4);
float swingFactorX = MathHelper.sin(swingAmount * MathHelper.PI);
float swingX = MathHelper.sin(state.handSwingProgress * MathHelper.PI) * (0.7F - head.pitch) * 0.75F;
float swingX = MathHelper.sin(handSwingProgress * MathHelper.PI) * (0.7F - head.pitch) * 0.75F;
ModelPart mainArm = getArm(mainHand);
mainArm.pitch -= swingFactorX * 1.2F + swingX;
mainArm.yaw += body.yaw * 2;
mainArm.roll -= MathHelper.sin(state.handSwingProgress * MathHelper.PI) * 0.4F;
mainArm.roll -= MathHelper.sin(handSwingProgress * MathHelper.PI) * 0.4F;
}
protected void rotateArm(ModelPart arm, ArmPose pose, float factor) {

View file

@ -1,54 +1,92 @@
package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.mob.EndermanEntity;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.client.model.entity.race.AlicornModel;
import com.minelittlepony.client.render.entity.EnderStallionRenderer;
import com.minelittlepony.api.pony.meta.Race;
public class EnderStallionModel extends SkeleponyModel<EndermanEntity> {
public boolean isCarrying;
public boolean isAttacking;
public boolean isAlicorn;
public boolean isBoss;
public class EnderStallionModel extends AlicornModel<EnderStallionRenderer.State> {
private final ModelPart leftHorn;
private final ModelPart rightHorn;
public EnderStallionModel(ModelPart tree) {
super(tree, false);
super(tree);
leftHorn = tree.getChild("left_horn");
rightHorn = tree.getChild("right_horn");
}
@Override
protected void setModelVisibilities(EnderStallionRenderer.State state) {
super.setModelVisibilities(state);
tail.setVisible(false, state);
snout.setVisible(false, state);
horn.setVisible(!state.isBoss, state);
leftHorn.visible = rightHorn.visible = state.isBoss;
public void animateModel(EndermanEntity entity, float move, float swing, float ticks) {
rightArmPose = isCarrying ? ArmPose.BLOCK : ArmPose.EMPTY;
leftArmPose = rightArmPose;
isUnicorn = true;
isAlicorn = entity.getUuid().getLeastSignificantBits() % 3 == 0;
isBoss = !isAlicorn && entity.getUuid().getLeastSignificantBits() % 90 == 0;
leftHorn.visible = rightHorn.visible = isBoss;
horn.setVisible(!isBoss, attributes);
}
@Override
public void setModelAngles(EnderStallionRenderer.State state) {
super.setModelAngles(state);
public void setModelAngles(EndermanEntity entity, float move, float swing, float ticks, float headYaw, float headPitch) {
super.setModelAngles(entity, move, swing, ticks, headYaw, headPitch);
if (state.isAttacking) {
if (isAttacking) {
head.pivotY -= 5;
}
}
@Override
public void transform(EnderStallionRenderer.State state, BodyPart part, MatrixStack stack) {
public void render(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) {
stack.push();
stack.translate(0, -1.15F, 0);
super.transform(state, part, stack);
super.render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha);
stack.pop();
}
@Override
public Race getRace() {
return isAlicorn ? (super.getRace().hasHorn() ? Race.ALICORN : Race.PEGASUS) : super.getRace();
}
@Override
public void rotateArmHolding(ModelPart arm, float direction, float swingProgress, float ticks) {
arm.pitch = -0.3707964F;
arm.pitch += 0.4F + MathHelper.sin(ticks * 0.067F) / 10;
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
tail.setVisible(false, attributes);
snout.setVisible(false, attributes);
leftSleeve.visible = false;
rightSleeve.visible = false;
leftPants.visible = false;
rightPants.visible = false;
}
@Override
public boolean wingsAreOpen() {
return isAttacking;
}
@Override
public float getWingRotationFactor(float ticks) {
return MathHelper.sin(ticks) + WINGS_HALF_SPREAD_ANGLE;
}
}

View file

@ -0,0 +1,46 @@
package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.render.entity.model.GuardianEntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.mob.GuardianEntity;
import com.minelittlepony.api.model.PonyModelMixin;
import com.minelittlepony.client.model.entity.race.SeaponyModel;
public class GuardianPonyModel extends GuardianEntityModel implements PonyModelMixin.Caster<GuardianEntity, SeaponyModel<GuardianEntity>, ModelPart> {
private final SeaponyModel<GuardianEntity> mixin;
public GuardianPonyModel(ModelPart tree) {
super(getTexturedModelData().createModel());
mixin = new SeaponyModel<>(tree);
}
@Override
public SeaponyModel<GuardianEntity> mixin() {
return mixin;
}
@Override
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) {
mixin().render(matrices, vertices, light, overlay, red, green, blue, alpha);
}
@Override
public void animateModel(GuardianEntity entity, float limbAngle, float limbDistance, float tickDelta) {
mixin().animateModel(entity, limbAngle, limbDistance, tickDelta);
}
@Override
public void copyStateTo(EntityModel<GuardianEntity> copy) {
mixin().copyStateTo(copy);
}
@Override
public void setAngles(GuardianEntity entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) {
mixin().setVisible(true);
mixin().setAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch);
}
}

View file

@ -6,30 +6,30 @@ import net.minecraft.util.Arm;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.client.model.entity.race.AlicornModel;
import com.minelittlepony.client.render.entity.npc.IllagerPonyRenderer;
public class IllagerPonyModel<S extends IllagerPonyRenderer.State> extends AlicornModel<S> {
public class IllagerPonyModel<T extends IllagerEntity> extends AlicornModel<T> {
public IllagerPonyModel(ModelPart tree) {
super(tree, false);
}
@Override
public void setModelAngles(S state) {
super.setModelAngles(state);
IllagerEntity.State pose = state.state;
public void setModelAngles(T illager, float move, float swing, float ticks, float headYaw, float headPitch) {
super.setModelAngles(illager, move, swing, ticks, headYaw, headPitch);
boolean rightHanded = state.mainArm == Arm.RIGHT;
IllagerEntity.State pose = illager.getState();
boolean rightHanded = illager.getMainArm() == Arm.RIGHT;
float mult = rightHanded ? 1 : -1;
ModelPart arm = getArm(state.mainArm);
ModelPart arm = getArm(illager.getMainArm());
if (pose == IllagerEntity.State.ATTACKING) {
// vindicator attacking
float f = MathHelper.sin(state.handSwingProgress * (float) Math.PI);
float f1 = MathHelper.sin((1 - (1 - state.handSwingProgress) * (1 - state.handSwingProgress)) * (float) Math.PI);
float f = MathHelper.sin(getSwingAmount() * (float) Math.PI);
float f1 = MathHelper.sin((1 - (1 - getSwingAmount()) * (1 - getSwingAmount())) * (float) Math.PI);
float cos = MathHelper.cos(state.age * 0.09F) * 0.05F + 0.05F;
float sin = MathHelper.sin(state.age * 0.067F) * 0.05F;
float cos = MathHelper.cos(ticks * 0.09F) * 0.05F + 0.05F;
float sin = MathHelper.sin(ticks * 0.067F) * 0.05F;
rightArm.roll = cos;
leftArm.roll = cos;
@ -37,7 +37,7 @@ public class IllagerPonyModel<S extends IllagerPonyRenderer.State> extends Alico
rightArm.yaw = 0.15707964F;
leftArm.yaw = -0.15707964F;
arm.pitch = -1.8849558F + MathHelper.cos(state.age * 0.09F) * 0.15F;
arm.pitch = -1.8849558F + MathHelper.cos(ticks * 0.09F) * 0.15F;
arm.pitch += f * 2.2F - f1 * 0.4F;
rightArm.pitch += sin;
@ -46,10 +46,10 @@ public class IllagerPonyModel<S extends IllagerPonyRenderer.State> extends Alico
// waving arms!
// rightArm.rotationPointZ = 0;
arm.pitch = (float) (-.75F * Math.PI);
arm.roll = mult * MathHelper.cos(state.age * 0.6662F) / 4;
arm.roll = mult * MathHelper.cos(ticks * 0.6662F) / 4;
arm.yaw = mult * 1.1F;
} else if (pose == IllagerEntity.State.BOW_AND_ARROW) {
aimBow(state, arm);
aimBow(arm, ticks);
}
}
}

View file

@ -2,12 +2,19 @@ package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.VexEntity;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.client.render.entity.VexRenderer;
import com.minelittlepony.common.util.animation.Interpolator;
public class ParaspriteModel<T extends LivingEntity> extends EntityModel<T> {
private final ModelPart root;
public class ParaspriteModel extends EntityModel<VexRenderer.State> {
private final ModelPart body;
private final ModelPart jaw;
private final ModelPart lips;
@ -17,45 +24,70 @@ public class ParaspriteModel extends EntityModel<VexRenderer.State> {
private final ModelPart leftWing2;
private final ModelPart rightWing2;
public ParaspriteModel(ModelPart root) {
super(root, RenderLayer::getEntityTranslucent);
body = root.getChild("body");
public ParaspriteModel(ModelPart tree) {
super(RenderLayer::getEntityTranslucent);
child = false;
root = tree;
body = tree.getChild("body");
jaw = body.getChild("jaw");
lips = body.getChild("lips");
leftWing = root.getChild("leftWing");
rightWing = root.getChild("rightWing");
leftWing2 = root.getChild("leftWing2");
rightWing2 = root.getChild("rightWing2");
leftWing = tree.getChild("leftWing");
rightWing = tree.getChild("rightWing");
leftWing2 = tree.getChild("leftWing2");
rightWing2 = tree.getChild("rightWing2");
}
@Override
public void setAngles(VexRenderer.State state) {
root.pitch = state.bodyPitch;
body.pitch = 0;
root.pitch = state.pitch * MathHelper.RADIANS_PER_DEGREE;
root.yaw = state.yawDegrees * MathHelper.RADIANS_PER_DEGREE;
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) {
root.render(matrices, vertices, light, overlay, red, green, blue, alpha);
}
jaw.pivotY = Math.max(0, 1.2F * state.jawOpenAmount);
@Override
public void setAngles(T entity, float move, float swing, float ticks, float headYaw, float headPitch) {
root.pitch = MathHelper.clamp((float)entity.getVelocity().horizontalLength() / 10F, 0, 0.1F);
body.pitch = 0;
if (entity.hasPassengers()) {
root.yaw = 0;
root.pitch = 0;
} else {
root.yaw = headYaw * 0.017453292F;
root.pitch = headPitch * 0.017453292F;
}
float sin = (float)Math.sin(ticks) / 2F;
float cos = (float)Math.cos(ticks) / 3F;
float jawOpenAmount = Interpolator.linear(entity.getUuid()).interpolate("jawOpen", entity instanceof VexEntity vex && vex.isCharging() ? 1 : 0, 10);
jaw.pivotY = Math.max(0, 1.2F * jawOpenAmount);
lips.pivotY = jaw.pivotY - 0.9F;
lips.visible = state.jawOpenAmount > 0;
body.pitch += 0.3F * state.jawOpenAmount;
jaw.pitch = 0.4F * state.jawOpenAmount;
lips.pitch = 0.2F * state.jawOpenAmount;
lips.visible = jawOpenAmount > 0;
body.pitch += 0.3F * jawOpenAmount;
jaw.pitch = 0.4F * jawOpenAmount;
lips.pitch = 0.2F * jawOpenAmount;
float basWingExpand = 1;
float innerWingExpand = basWingExpand / 2F;
leftWing.pitch = 0;
leftWing.roll = state.wingRoll;
leftWing.yaw = state.wingYaw;
leftWing.roll = basWingExpand + cos + 0.3F;
leftWing.yaw = basWingExpand - sin;
rightWing.pitch = 0;
rightWing.roll = -state.wingRoll;
rightWing.yaw = -state.wingYaw;
rightWing.roll = -basWingExpand - cos - 0.3F;
rightWing.yaw = -basWingExpand + sin;
sin = -(float)Math.sin(ticks + Math.PI / 4F) / 2F;
cos = (float)Math.cos(ticks + Math.PI / 4F) / 3F;
leftWing2.pitch = 0;
leftWing2.roll = state.innerWingRoll;
leftWing2.yaw = state.innerWingPitch;
leftWing2.roll = innerWingExpand + sin - 0.3F;
leftWing2.yaw = innerWingExpand - cos + 0.3F;
rightWing2.pitch = 0;
rightWing2.roll = -state.innerWingRoll;
rightWing2.yaw = -state.innerWingPitch;
rightWing2.roll = -innerWingExpand - sin + 0.3F;
rightWing2.yaw = -innerWingExpand + cos - 0.3F;
}
}

View file

@ -1,12 +1,17 @@
package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.entity.mob.AbstractPiglinEntity;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.mob.PiglinActivity;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.client.render.entity.PonyPiglinRenderer;
import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.pony.Pony;
public class PiglinPonyModel extends ZomponyModel<PonyPiglinRenderer.State> {
public class PiglinPonyModel extends ZomponyModel<HostileEntity> {
private PiglinActivity activity;
private final ModelPart leftFlap;
private final ModelPart rightFlap;
@ -18,39 +23,53 @@ public class PiglinPonyModel extends ZomponyModel<PonyPiglinRenderer.State> {
}
@Override
public void setModelAngles(PonyPiglinRenderer.State state) {
super.setModelAngles(state);
public void updateLivingState(HostileEntity entity, Pony pony, ModelAttributes.Mode mode) {
super.updateLivingState(entity, pony, mode);
leftArmPose = ArmPose.EMPTY;
rightArmPose = entity.getMainHandStack().isEmpty() ? ArmPose.EMPTY : ArmPose.ITEM;
float progress = state.age * 0.1F + state.limbFrequency * 0.5F;
float range = 0.08F + state.limbAmplitudeMultiplier * 0.4F;
if (entity instanceof AbstractPiglinEntity) {
activity = ((AbstractPiglinEntity)entity).getActivity();
if (activity == PiglinActivity.CROSSBOW_HOLD) {
rightArmPose = ArmPose.CROSSBOW_HOLD;
} else if (activity == PiglinActivity.CROSSBOW_CHARGE) {
rightArmPose = ArmPose.CROSSBOW_CHARGE;
} else if (activity == PiglinActivity.ADMIRING_ITEM) {
leftArmPose = ArmPose.ITEM;
}
} else {
activity = PiglinActivity.DEFAULT;
}
}
@Override
public void setModelAngles(HostileEntity entity, float move, float swing, float ticks, float headYaw, float headPitch) {
super.setModelAngles(entity, move, swing, ticks, headYaw, headPitch);
float progress = ticks * 0.1F + move * 0.5F;
float range = 0.08F + swing * 0.4F;
rightFlap.roll = -0.5235988F - MathHelper.cos(progress * 1.2F) * range;
leftFlap.roll = 0.5235988F + MathHelper.cos(progress) * range;
}
@Override
public void setHeadRotation(float animationProgress, float yaw, float pitch) {
super.setHeadRotation(animationProgress, yaw, pitch);
leftFlap.roll = -(float)(-(Math.cos((double)(animationProgress * (float) Math.PI * 0.2F * 1.2F)) + 2.5)) * 0.2F;
rightFlap.roll = -(float)(Math.cos((double)(animationProgress * (float) Math.PI * 0.2F)) + 2.5) * 0.2F;
}
protected void rotateLegs(float move, float swing, float ticks, HostileEntity entity) {
super.rotateLegs(move, swing, ticks, entity);
@Override
protected void rotateLegs(PonyPiglinRenderer.State state) {
super.rotateLegs(state);
if (state.activity == PiglinActivity.ADMIRING_ITEM) {
if (activity == PiglinActivity.ADMIRING_ITEM) {
leftArm.yaw = 0.5F;
leftArm.pitch = -1.9F;
leftArm.pivotY += 4;
leftArm.pivotZ += 3;
leftArm.pivotX += 2;
head.pitch = MathHelper.sin(state.age / 12) / 6 + 0.5F;
head.pitch = MathHelper.sin(ticks / 12) / 6 + 0.5F;
head.yaw = 0;
head.roll = MathHelper.sin(state.age / 10) / 3F;
} else if (state.activity == PiglinActivity.DANCING) {
head.roll = MathHelper.sin(ticks / 10) / 3F;
} else if (activity == PiglinActivity.DANCING) {
float speed = state.age / 60;
float speed = ticks / 60;
head.pivotX = MathHelper.sin(speed * 10);
head.pivotY = MathHelper.sin(speed * 40) + 0.4F;
@ -74,7 +93,7 @@ public class PiglinPonyModel extends ZomponyModel<PonyPiglinRenderer.State> {
}
@Override
protected boolean shouldLiftBothArms(PonyPiglinRenderer.State state) {
return state.zombified && super.shouldLiftBothArms(state);
protected boolean isZombified(HostileEntity entity) {
return !(entity instanceof AbstractPiglinEntity);
}
}

View file

@ -0,0 +1,36 @@
package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.entity.mob.IllagerEntity;
import net.minecraft.entity.mob.PillagerEntity;
import net.minecraft.util.Arm;
import com.minelittlepony.client.model.entity.race.ChangelingModel;
public class PillagerPonyModel<T extends PillagerEntity> extends ChangelingModel<T> {
public PillagerPonyModel(ModelPart tree) {
super(tree, false);
}
@Override
public void animateModel(T entity, float move, float swing, float ticks) {
ArmPose holdingPose = getHoldingPose(entity.getState());
if (holdingPose != ArmPose.EMPTY) {
boolean rightHanded = entity.getMainArm() == Arm.RIGHT;
leftArmPose = rightHanded ? ArmPose.EMPTY : holdingPose;
rightArmPose = rightHanded ? holdingPose : ArmPose.EMPTY;
}
}
protected ArmPose getHoldingPose(IllagerEntity.State state) {
switch (state) {
case BOW_AND_ARROW: return ArmPose.BOW_AND_ARROW;
case CROSSBOW_CHARGE: return ArmPose.CROSSBOW_CHARGE;
case CROSSBOW_HOLD: return ArmPose.CROSSBOW_HOLD;
default: return ArmPose.EMPTY;
}
}
}

View file

@ -3,30 +3,42 @@ package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.ArmorStandEntityModel;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.state.ArmorStandEntityRenderState;
import net.minecraft.entity.decoration.ArmorStandEntity;
import net.minecraft.util.math.EulerAngle;
import com.minelittlepony.mson.util.PartUtil;
public class PonyArmourStandModel extends ArmorStandEntityModel {
private static final EulerAngle DEFAULT_LEFT_LEG_ROTATION = new EulerAngle(-1, 0, -1);
private static final EulerAngle DEFAULT_RIGHT_LEG_ROTATION = new EulerAngle(1, 0, 1);
public PonyArmourStandModel(ModelPart modelPart) {
super(modelPart);
}
@Override
public void copyTransforms(BipedEntityModel<ArmorStandEntityRenderState> model) {
if (model instanceof PonyArmourStandModel) {
super.copyTransforms(model);
} else {
PartUtil.copyAngles(head, model.head);
PartUtil.copyAngles(rightArm, model.rightArm);
PartUtil.copyAngles(leftArm, model.leftArm);
PartUtil.copyAngles(rightLeg, model.rightLeg);
PartUtil.copyAngles(leftLeg, model.leftLeg);
public void setAngles(ArmorStandEntity entity, float move, float swing, float ticks, float headYaw, float headPitch) {
super.setAngles(entity, move, swing, ticks, headYaw, headPitch);
leftArm.visible = true;
rightArm.visible = true;
if (entity.getLeftLegRotation().equals(DEFAULT_LEFT_LEG_ROTATION)) {
PartUtil.copyAngles(leftArm, leftLeg);
leftLeg.pitch *= -1;
}
if (entity.getRightLegRotation().equals(DEFAULT_RIGHT_LEG_ROTATION)) {
PartUtil.copyAngles(rightArm, rightLeg);
rightLeg.pitch *= -1;
}
}
@Override
public void setAngles(ArmorStandEntityRenderState state) {
super.setAngles(state);
public void applyAnglesTo(BipedEntityModel<ArmorStandEntity> dest) {
PartUtil.copyAngles(head, dest.head);
PartUtil.copyAngles(hat, dest.hat);
PartUtil.copyAngles(leftLeg, dest.leftLeg);
PartUtil.copyAngles(rightLeg, dest.rightLeg);
PartUtil.copyAngles(leftArm, dest.leftArm);
PartUtil.copyAngles(rightArm, dest.rightArm);
}
}

View file

@ -1,18 +1,27 @@
package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.client.render.entity.StriderRenderer;
public class SaddleModel<T extends LivingEntity> extends EntityModel<T> {
private ModelPart root;
public class SaddleModel extends EntityModel<StriderRenderer.State> {
public SaddleModel(ModelPart tree) {
super(tree);
root = tree;
}
@Override
public void setAngles(StriderRenderer.State entity) {
root.pivotY = 2 - MathHelper.cos(entity.limbFrequency * 1.5F) * 3 * entity.limbAmplitudeMultiplier;
public void setAngles(T entity, float move, float swing, float ticks, float headYaw, float headPitch) {
root.pivotY = 2 - MathHelper.cos(move * 1.5f) * 3.0f * swing;
}
@Override
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) {
root.render(matrices, vertices, light, overlay, red, green, blue, alpha);
}
}

View file

@ -0,0 +1,85 @@
package com.minelittlepony.client.model.entity;
import net.minecraft.entity.mob.WitherSkeletonEntity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.item.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Arm;
import net.minecraft.util.Hand;
import com.minelittlepony.api.model.MobPosingHelper;
import com.minelittlepony.client.model.entity.race.AlicornModel;
public class SkeleponyModel<T extends HostileEntity> extends AlicornModel<T> {
public boolean isUnicorn;
public boolean isWithered;
public SkeleponyModel(ModelPart tree) {
super(tree, false);
this.vestRenderList.clear();
this.sleevesRenderList.clear();
}
@Override
public void animateModel(T entity, float move, float swing, float ticks) {
isUnicorn = entity.getUuid().getLeastSignificantBits() % 3 != 0;
isWithered = entity instanceof WitherSkeletonEntity;
rightArmPose = ArmPose.EMPTY;
leftArmPose = ArmPose.EMPTY;
ItemStack mainHand = entity.getStackInHand(Hand.MAIN_HAND);
ItemStack offHand = entity.getStackInHand(Hand.OFF_HAND);
boolean right = entity.getMainArm() == Arm.RIGHT;
if (!offHand.isEmpty()) {
if (right) {
leftArmPose = ArmPose.ITEM;
} else {
rightArmPose = ArmPose.ITEM;
}
}
if (!mainHand.isEmpty()) {
ArmPose pose = mainHand.getItem() == Items.BOW && entity.isAttacking() ? ArmPose.BOW_AND_ARROW : ArmPose.ITEM;
if (right) {
rightArmPose = pose;
} else {
leftArmPose = pose;
}
}
}
@Override
protected void rotateLegs(float move, float swing, float ticks, T entity) {
super.rotateLegs(move, swing, ticks, entity);
if (rightArmPose != ArmPose.EMPTY) {
rotateArmHolding(getArm(Arm.RIGHT), -1, getSwingAmount(), ticks);
}
if (leftArmPose != ArmPose.EMPTY) {
rotateArmHolding(getArm(Arm.LEFT), -1, getSwingAmount(), ticks);
}
}
protected void rotateArmHolding(ModelPart arm, float direction, float swingProgress, float ticks) {
MobPosingHelper.rotateArmHolding(arm, direction, swingProgress, ticks);
}
@Override
public boolean hasMagic() {
return isUnicorn;
}
@Override
protected float getLegOutset() {
if (attributes.isLyingDown) return 2.6f;
if (attributes.isCrouching) return 0;
return 4;
}
}

View file

@ -2,10 +2,11 @@ package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.passive.StriderEntity;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.client.render.entity.StriderRenderer;
public class SpikeModel extends BipedEntityModel<StriderRenderer.State> {
public class SpikeModel<T extends LivingEntity> extends BipedEntityModel<T> {
private final ModelPart tail;
private final ModelPart tail2;
@ -19,12 +20,22 @@ public class SpikeModel extends BipedEntityModel<StriderRenderer.State> {
}
@Override
public void setAngles(StriderRenderer.State entity) {
super.setAngles(entity);
public void setAngles(T entity, float move, float swing, float ticks, float headYaw, float headPitch) {
swing *= 2;
move *= 1.5F;
child = false;
head.pivotX = 0;
head.pivotZ = 0;
head.pivotY = 0;
super.setAngles(entity, move, swing, ticks, headYaw, headPitch);
leftArm.pivotY++;
rightArm.pivotY++;
body.pitch += 0.15F;
if (entity.saddled) {
if ((entity instanceof StriderEntity strider && strider.isSaddled())) {
leftArm.pitch = 3.15F;
leftArm.yaw = 1;
rightArm.pitch = 3.15F;
@ -38,8 +49,10 @@ public class SpikeModel extends BipedEntityModel<StriderRenderer.State> {
leftLeg.pitch += 0.4F;
rightLeg.pitch += 0.4F;
} else {
leftArm.roll -= 0.2F * entity.flailAmount;
rightArm.roll += 0.2F * entity.flailAmount;
float flailAmount = 1 + (float)MathHelper.clamp(entity.getVelocity().y * 10, 0, 7);
leftArm.roll -= 0.2F * flailAmount;
rightArm.roll += 0.2F * flailAmount;
leftArm.pivotZ += 2;
leftArm.pitch -= 0.3F;
@ -47,8 +60,8 @@ public class SpikeModel extends BipedEntityModel<StriderRenderer.State> {
rightArm.pivotZ += 2;
rightArm.pitch -= 0.3F;
if (entity.cold) {
float armMotion = (float)Math.sin(entity.age / 10F) / 10F;
if (entity instanceof StriderEntity strider && strider.isCold()) {
float armMotion = (float)Math.sin(ticks / 10F) / 10F;
leftArm.pitch = -1 - armMotion;
rightArm.pitch = -1 + armMotion;
@ -61,13 +74,21 @@ public class SpikeModel extends BipedEntityModel<StriderRenderer.State> {
}
}
tail.pitch = (float)Math.sin(entity.limbFrequency) / 3F - 0.5F;
tail.pitch = (float)Math.sin(move) / 3F - 0.5F;
tail2.pitch = -tail.pitch / 2;
tail3.pitch = tail2.pitch / 2;
tail.yaw = (float)Math.sin(entity.age / 20F) / 40 + (float)Math.sin(entity.limbFrequency / 20F) / 4;
tail.yaw = (float)Math.sin(ticks / 20F) / 40 + (float)Math.sin(move / 20F) / 4;
tail2.yaw = tail.yaw / 2;
tail3.yaw = tail2.yaw / 2;
for (var part : getHeadParts()) {
part.pivotY += 7;
}
for (var part : getBodyParts()) {
part.pivotY += 7;
}
}
}

Some files were not shown because too many files have changed in this diff Show more