Compare commits

..

4 commits

Author SHA1 Message Date
Sollace
53e9a1f9da Add some compatibility for unicopia 2023-04-11 17:25:48 +02:00
Sollace
d881fc1a5d Fixed hippogriffs not using the correct models 2023-04-11 16:55:25 +02:00
Sollace
ea89829050 Comment bigpony dependency 2023-04-10 22:55:08 +02:00
Sollace
478e7b56a8 1.19.4 -> 1.19.3 2023-04-10 22:51:25 +02:00
234 changed files with 3399 additions and 3236 deletions

View file

@ -1,12 +1,10 @@
# Mine Little Pony # Mine Little Pony
[![Current Version](https://img.shields.io/github/v/release/MineLittlePony/MineLittlePony)](https://github.com/MineLittlePony/MineLittlePony/releases/latest) [![Current Version](https://img.shields.io/github/v/tag/MineLittlePony/MineLittlePony)](https://img.shields.io/github/v/tag/MineLittlePony/MineLittlePony)
[![Build Status](https://github.com/MineLittlePony/MineLittlePony/actions/workflows/gradle-build.yml/badge.svg)](https://github.com/MineLittlePony/MineLittlePony/actions/workflows/gradle-build.yml) [![Build Status](https://github.com/MineLittlePony/MineLittlePony/actions/workflows/gradle-build.yml/badge.svg)](https://github.com/MineLittlePony/MineLittlePony/actions/workflows/gradle-build.yml)
![Downloads](https://img.shields.io/github/downloads/MineLittlePony/MineLittlePony/total.svg?color=yellowgreen) ![Downloads](https://img.shields.io/github/downloads/MineLittlePony/MineLittlePony/total.svg?color=yellowgreen)
[![Modrinth](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.modrinth.com%2Fv2%2Fproject%2Fmine-little-pony%2Fversion&query=%24%5B0%5D.version_number&label=modrinth)](https://modrinth.com/mod/mine-little-pony)
[![Discord Server](https://img.shields.io/discord/182490536119107584.svg?color=blueviolet)](https://discord.gg/HbJSFyu) [![Discord Server](https://img.shields.io/discord/182490536119107584.svg?color=blueviolet)](https://discord.gg/HbJSFyu)
![License](https://img.shields.io/github/license/MineLittlePony/MineLittlePony) ![License](https://img.shields.io/github/license/MineLittlePony/MineLittlePony)
[![Crowdin](https://badges.crowdin.net/mine-little-pony/localized.svg)](https://crowdin.com/project/mine-little-pony)
![](https://img.shields.io/badge/api-fabric-orange.svg) ![](https://img.shields.io/badge/api-fabric-orange.svg)
Turns players and mobs into ponies. Turns players and mobs into ponies.

View file

@ -1,15 +1,16 @@
buildscript { buildscript {
dependencies { dependencies {
classpath 'com.github.dexman545:Outlet:1.6.1' classpath 'com.github.dexman545:Outlet:1.3.10'
} }
} }
plugins { plugins {
id 'fabric-loom' version '1.5-SNAPSHOT' id 'java-library'
id 'fabric-loom' version '0.12-SNAPSHOT'
id 'maven-publish' id 'maven-publish'
id 'com.modrinth.minotaur' version '2.+' id 'com.modrinth.minotaur' version '2.+'
id 'org.ajoberstar.reckon' version '0.13.1' id 'org.ajoberstar.reckon' version '0.13.0'
} }
apply plugin: 'io.github.dexman545.outlet' apply plugin: 'dex.plugins.outlet'
java { java {
toolchain { toolchain {
@ -37,9 +38,18 @@ reckon {
repositories { repositories {
mavenLocal() mavenLocal()
maven { name 'modmenu'; url 'https://maven.terraformersmc.com/releases' } maven {
maven { name 'minelp'; url 'https://repo.minelittlepony-mod.com/maven/snapshot' } name = 'modmenu'
maven { name 'minelp-release'; url 'https://repo.minelittlepony-mod.com/maven/release' } url = 'https://maven.terraformersmc.com/releases'
}
maven {
name = 'minelp'
url = 'https://repo.minelittlepony-mod.com/maven/snapshot'
}
maven {
name = 'minelp-release'
url = 'https://repo.minelittlepony-mod.com/maven/release'
}
} }
dependencies { dependencies {
@ -59,7 +69,8 @@ dependencies {
include "com.minelittlepony:mson:${project.mson_version}" include "com.minelittlepony:mson:${project.mson_version}"
modImplementation "com.minelittlepony:hdskins:${project.hd_skins_version}" modImplementation "com.minelittlepony:hdskins:${project.hd_skins_version}"
modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}" // modImplementation "com.minelittlepony:bigpony:${project.bigpony_version}"
modCompileOnly("com.terraformersmc:modmenu:${project.modmenu_version}")
} }
// //
@ -83,12 +94,7 @@ processResources {
inputs.property "version", project.version.toString() inputs.property "version", project.version.toString()
filesMatching("fabric.mod.json") { filesMatching("fabric.mod.json") {
expand "version": project.version.toString(), expand "version": project.version.toString()
"minecraftVersion": project.minecraft_version_range,
"loaderVersion": ">=${project.loader_version}",
"fabricVersion": ">=${project.fabric_version}",
"kirinVersion": ">=${project.kirin_version}",
"msonVersion": ">=${project.mson_version}"
} }
from 'LICENSE' from 'LICENSE'
@ -117,7 +123,6 @@ modrinth {
versionNumber = version.toString() versionNumber = version.toString()
versionName = archivesBaseName + '-' + version versionName = archivesBaseName + '-' + version
changelog = "[Changelog](https://github.com/MineLittlePony/MineLittlePony/releases/tag/${version.toString()})" changelog = "[Changelog](https://github.com/MineLittlePony/MineLittlePony/releases/tag/${version.toString()})"
loaders = ['fabric', 'quilt']
uploadFile = remapJar uploadFile = remapJar
outlet.mcVersions().each{ver -> outlet.mcVersions().each{ver ->
gameVersions.add ver gameVersions.add ver

View file

@ -1,3 +0,0 @@
files:
- source: src/main/resources/assets/minelittlepony/lang/en_us.json
translation: /%original_path%/%locale_with_underscore%.%file_extension%

View file

@ -3,10 +3,10 @@ org.gradle.daemon=false
# Fabric Properties # Fabric Properties
# check these on https://fabricmc.net/develop # check these on https://fabricmc.net/develop
minecraft_version=1.20.2 minecraft_version=1.19.3
yarn_mappings=1.20.2+build.4 yarn_mappings=1.19.3+build.5
loader_version=0.15.1 loader_version=0.14.19
fabric_version=0.91.1+1.20.2 fabric_version=0.76.0+1.19.3
# Mod Properties # Mod Properties
group=com.minelittlepony group=com.minelittlepony
@ -15,12 +15,13 @@ org.gradle.daemon=false
description=Mine Little Pony turns players and mobs into ponies. Press F9 ingame to access settings. description=Mine Little Pony turns players and mobs into ponies. Press F9 ingame to access settings.
# Publishing # Publishing
minecraft_version_range=>=1.20.2 minecraft_version_range=1.19.3
modrinth_loader_type=fabric modrinth_loader_type=fabric
modrinth_project_id=JBjInUXM modrinth_project_id=JBjInUXM
# Dependencies # Dependencies
modmenu_version=8.0.0 modmenu_version=5.0.0-alpha.3
kirin_version=1.16.1+1.20.2 kirin_version=1.13.2
hd_skins_version=6.12.4+1.20.2 hd_skins_version=6.7.6
mson_version=1.9.3+1.20.2 bigpony_version=1.8.0+lts
mson_version=1.8.0+lts.1.19.3

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View file

@ -1,8 +1,14 @@
pluginManagement { pluginManagement {
repositories { repositories {
mavenCentral() mavenCentral()
maven { name 'Fabric'; url 'https://maven.fabricmc.net/' } maven {
maven { name 'Jitpack'; url 'https://jitpack.io' } name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
maven {
name = 'Jitpack'
url = 'https://jitpack.io'
}
gradlePluginPortal() gradlePluginPortal()
} }
} }

2
skins

@ -1 +1 @@
Subproject commit 45dfe1101b4506077b3d3f2246b08c9b485735eb Subproject commit 357cd1775f9bbf1620ca97c5f7e5b59d0e9ec4c4

View file

@ -2,8 +2,8 @@ package com.minelittlepony.api.config;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import com.minelittlepony.api.pony.meta.*; import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.common.client.gui.VisibilityMode; import com.minelittlepony.api.pony.meta.Sizes;
import com.minelittlepony.common.util.GamePaths; import com.minelittlepony.common.util.GamePaths;
import com.minelittlepony.common.util.settings.*; import com.minelittlepony.common.util.settings.*;
@ -26,13 +26,13 @@ public class PonyConfig extends Config {
* Sets the pony level. Want MOAR PONEHS? Well here you go. * Sets the pony level. Want MOAR PONEHS? Well here you go.
*/ */
public final Setting<PonyLevel> ponyLevel = value("ponylevel", PonyLevel.PONIES) public final Setting<PonyLevel> ponyLevel = value("ponylevel", PonyLevel.PONIES)
.addComment("How much pony do you want?") .addComment("How much pony do you want?")
.addComment("PONIES - all players are turned into ponies") .addComment("PONIES - all players are turned into ponies")
.addComment("HUMANS - all players are humans") .addComment("HUMANS - all players are humans")
.addComment("BOTH - players with compatible skins will be ponies whilst the rest are humans"); .addComment("BOTH - players with compatible skins will be ponies whilst the rest are humans");
private final Setting<Float> scaleFactor = value("globalScaleFactor", 0.9F) private final Setting<Float> scaleFactor = value("globalScaleFactor", 0.9F)
.addComment("How large do you want your ponies to be?") .addComment("How large do you want your ponies to be?")
.addComment("Default is show scale (0.9)"); .addComment("Default is show scale (0.9)");
public final Setting<Boolean> sizes = value("settings", "sizes", true) public final Setting<Boolean> sizes = value("settings", "sizes", true)
.addComment("Allows ponies of different sizes/ages"); .addComment("Allows ponies of different sizes/ages");
@ -53,14 +53,9 @@ public class PonyConfig extends Config {
.addComment("Adjust camera intersection checks to properly cull entities when they're not in view.") .addComment("Adjust camera intersection checks to properly cull entities when they're not in view.")
.addComment("Helps to prevent entities from vanishing when they're in long stacks"); .addComment("Helps to prevent entities from vanishing when they're in long stacks");
public final Setting<Boolean> horsieMode = value("settings", "horsieMode", false) public final Setting<Boolean> horsieMode = value("settings", "horsieMode", false)
.addComment("Enables the alternative horsey models from the April Fools 2023 update"); .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) public final Setting<Sizes> sizeOverride = value("debug", "sizeOverride", Sizes.UNSET)
.addComment("Overrides pony sizes") .addComment("Overrides pony sizes")
.addComment("Possible values: TALL, BULKY, LANKY, NORMAL, YEARLING, FOAL, UNSET (default)"); .addComment("Possible values: TALL, BULKY, LANKY, NORMAL, YEARLING, FOAL, UNSET (default)");
@ -74,17 +69,10 @@ public class PonyConfig extends Config {
public final Setting<Boolean> flappyElytras = value("customisation", "flappyElytras", false) public final Setting<Boolean> flappyElytras = value("customisation", "flappyElytras", false)
.addComment("Pegasi will use their wings to fly even when they're wearing an elytra"); .addComment("Pegasi will use their wings to fly even when they're wearing an elytra");
public final Setting<Boolean> noFun = value("customisation", "noFun", false) public final Setting<Boolean> noFun = value("customisation", "noFun", false)
.addComment("Disables certain easter eggs and secrets (party pooper)") .addComment("Disables certain easter eggs and secrets (party pooper)")
.addComment("Turning this off may help with compatibility in some cases"); .addComment("Turning this off may help with compatibility in some cases");
public final Setting<VisibilityMode> horseButton = value("horseButton", VisibilityMode.AUTO)
.addComment("Whether to show the mine little pony settings button on the main menu")
.addComment("AUTO (default) - only show when HDSkins is not installed")
.addComment("ON - always show")
.addComment("OFF - never show");
public PonyConfig(Path path) { public PonyConfig(Path path) {
super(HEIRARCHICAL_JSON_ADAPTER, path); super(HEIRARCHICAL_JSON_ADAPTER, path);
instance = this; instance = this;
@ -143,18 +131,4 @@ public class PonyConfig extends Config {
return race; return race;
} }
public static Size getEffectiveSize(Size size) {
SizePreset sz = instance.sizeOverride.get();
if (sz != SizePreset.UNSET) {
return sz;
}
if (size == SizePreset.UNSET || !instance.sizes.get()) {
return SizePreset.NORMAL;
}
return size;
}
} }

View file

@ -1,73 +0,0 @@
package com.minelittlepony.api.events;
import net.minecraft.network.PacketByteBuf;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.api.pony.meta.*;
public class MsgPonyData {
private static final short API_IDENTIFIER = (short) 0xABCD;
// API version - increment this number before any time any data is added/removed/moved in the data stream
private static final byte API_VERSION = 3;
public static PonyData read(PacketByteBuf buffer) {
short data = buffer.readShort();
if (data != API_IDENTIFIER || buffer.readByte() != API_VERSION) {
return PonyData.NULL;
}
return new PonyData(
buffer.readEnumConstant(Race.class),
buffer.readEnumConstant(TailLength.class),
buffer.readEnumConstant(TailShape.class),
buffer.readEnumConstant(Gender.class),
new MsgSize(buffer),
buffer.readInt(),
buffer.readBoolean(),
buffer.readVarInt(),
Flags.read(Wearable.NONE, buffer)
);
}
public static PacketByteBuf write(PonyData data, PacketByteBuf buffer) {
buffer.writeShort(API_IDENTIFIER);
buffer.writeByte(API_VERSION);
buffer.writeEnumConstant(data.race());
buffer.writeEnumConstant(data.tailLength());
buffer.writeEnumConstant(data.tailShape());
buffer.writeEnumConstant(data.gender());
write(data.size(), buffer);
buffer.writeInt(data.glowColor());
buffer.writeBoolean(data.noSkin());
buffer.writeVarInt(data.priority());
data.gear().write(buffer);
return buffer;
}
private static void write(Size size, PacketByteBuf buffer) {
buffer.writeInt(size.ordinal());
buffer.writeString(size.name());
buffer.writeFloat(size.shadowSize());
buffer.writeFloat(size.scaleFactor());
buffer.writeFloat(size.eyeHeightFactor());
buffer.writeFloat(size.eyeDistanceFactor());
buffer.writeFloat(size.colorCode());
}
private record MsgSize (
int ordinal,
String name,
float shadowSize,
float scaleFactor,
float eyeHeightFactor,
float eyeDistanceFactor,
int colorCode) implements Size {
MsgSize(PacketByteBuf buffer) {
this(buffer.readInt(), buffer.readString(), buffer.readFloat(), buffer.readFloat(), buffer.readFloat(), buffer.readFloat(), buffer.readInt());
}
@Override
public String toString() {
return name;
}
}
}

View file

@ -1,17 +0,0 @@
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

@ -0,0 +1,11 @@
package com.minelittlepony.api.model;
/**
* Interface for models that have a head.
*/
public interface ICapitated<T> {
/**
* Gets the head of this capitated object.
*/
T getHead();
}

View file

@ -1,49 +1,66 @@
package com.minelittlepony.api.model; package com.minelittlepony.api.model;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.*;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import com.minelittlepony.api.config.PonyConfig; import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.pony.Pony; import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.api.pony.meta.*; import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.mson.api.MsonModel;
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);
public interface IModel {
/** /**
* Applies a transform particular to a certain body part. * Applies a transform particular to a certain body part.
*/ */
void transform(BodyPart part, MatrixStack stack); void transform(BodyPart part, MatrixStack stack);
/**
* Gets the active scaling profile used to lay out this model's parts.
*/
Size getSize();
/** /**
* Gets the transitive properties of this model. * Gets the transitive properties of this model.
*/ */
ModelAttributes getAttributes(); ModelAttributes getAttributes();
/** /**
* Sets the pony metadata object associated with this model. * Gets the skin metadata associated with this model.
*/ */
void setMetadata(PonyData meta); default IPonyData getMetadata() {
return getAttributes().metadata;
}
/** /**
* Gets the active scaling profile used to lay out this model's parts. * Sets the pony metadata object associated with this model.
*/ */
default Size getSize() { void setMetadata(IPonyData meta);
return PonyConfig.getEffectiveSize(getAttributes().metadata.size());
/**
* Returns true if the model is flying.
*/
default boolean isFlying() {
return getAttributes().isFlying && canFly();
}
/**
* Returns true if this model is riding a boat, horse, or other animals.
*
* @deprecated User model#getAttributes().isSitting
*/
@Deprecated
default boolean isRiding() {
return getAttributes().isSitting;
} }
default Race getRace() { default Race getRace() {
return PonyConfig.getEffectiveRace(getAttributes().metadata.race()); return PonyConfig.getEffectiveRace(getMetadata().getRace());
}
/**
* Returns true if this model is being applied to a race that has wings.
*/
default boolean canFly() {
return getRace().hasWings();
} }
/** /**
@ -55,6 +72,7 @@ public interface PonyModel<T extends LivingEntity> extends MsonModel, ModelWithH
* Gets the step wobble used for various hair bits and animations. * Gets the step wobble used for various hair bits and animations.
*/ */
default float getWobbleAmount() { default float getWobbleAmount() {
if (getSwingAmount() <= 0) { if (getSwingAmount() <= 0) {
return 0; return 0;
} }
@ -79,7 +97,6 @@ public interface PonyModel<T extends LivingEntity> extends MsonModel, ModelWithH
* i.e. Used to change wing rendering when using saddlebags. * i.e. Used to change wing rendering when using saddlebags.
*/ */
default boolean isEmbedded(Wearable wearable) { default boolean isEmbedded(Wearable wearable) {
return getAttributes().metadata.gear().matches(wearable); return getMetadata().isWearing(wearable);
} }
} }

View file

@ -0,0 +1,10 @@
package com.minelittlepony.api.model;
import com.minelittlepony.api.pony.IPonyData;
public interface IModelWrapper {
/**
* Updates metadata values to this wrapper's contained models.
*/
IModelWrapper applyMetadata(IPonyData meta);
}

View file

@ -3,7 +3,9 @@ package com.minelittlepony.api.model;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
public interface SubModel { import java.util.UUID;
public interface IPart {
/** /**
* Sets the model's various rotation angles. * Sets the model's various rotation angles.
*/ */
@ -11,6 +13,13 @@ public interface SubModel {
} }
@Deprecated
default void setRotationAndAngles(boolean goingFast, UUID interpolatorId, float limbAngle, float limbSpeed, float bodySwing, float animationProgress) {
Compat.attributes.isGoingFast = goingFast;
Compat.attributes.interpolatorId = interpolatorId;
setPartAngles(Compat.attributes, limbAngle, limbSpeed, bodySwing, animationProgress);
}
/** /**
* Renders this model component. * Renders this model component.
*/ */
@ -22,4 +31,14 @@ public interface SubModel {
default void setVisible(boolean visible, ModelAttributes attributes) { default void setVisible(boolean visible, ModelAttributes attributes) {
} }
@Deprecated
default void setVisible(boolean visible) {
setVisible(visible, Compat.attributes);
}
@Deprecated
class Compat {
public static ModelAttributes attributes = new ModelAttributes();
}
} }

View file

@ -1,12 +1,10 @@
package com.minelittlepony.api.model; package com.minelittlepony.api.model;
import net.minecraft.entity.LivingEntity;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.util.MathUtil; import com.minelittlepony.util.MathUtil;
public interface WingedPonyModel<T extends LivingEntity> extends PonyModel<T> { public interface IPegasus extends IModel {
public static final float WINGS_HALF_SPREAD_ANGLE = MathUtil.Angles._270_DEG; 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_FULL_SPREAD_ANGLE = MathUtil.Angles._270_DEG + 0.4F;
public static final float WINGS_RAISED_ANGLE = 4; public static final float WINGS_RAISED_ANGLE = 4;
@ -15,20 +13,18 @@ public interface WingedPonyModel<T extends LivingEntity> extends PonyModel<T> {
* Returns true if the wings are spread. * Returns true if the wings are spread.
*/ */
default boolean wingsAreOpen() { default boolean wingsAreOpen() {
return (getAttributes().isSwimming || getAttributes().isFlying || getAttributes().isCrouching) return (getAttributes().isSwimming || isFlying() || getAttributes().isCrouching)
&& (PonyConfig.getInstance().flappyElytras.get() || !getAttributes().isGliding); && (MineLittlePony.getInstance().getConfig().flappyElytras.get() || !getAttributes().isGliding);
} }
default boolean isBurdened() { default boolean isBurdened() {
return isWearing(Wearable.SADDLE_BAGS_BOTH) return isWearing(Wearable.SADDLE_BAGS_BOTH) || isWearing(Wearable.SADDLE_BAGS_LEFT) || isWearing(Wearable.SADDLE_BAGS_RIGHT);
|| isWearing(Wearable.SADDLE_BAGS_LEFT)
|| isWearing(Wearable.SADDLE_BAGS_RIGHT);
} }
/** /**
* Gets the wings of this pegasus/flying creature * Gets the wings of this pegasus/flying creature
*/ */
SubModel getWings(); IPart getWings();
/** /**
* Determines angle used to animate wing flaps whilst flying/swimming. * Determines angle used to animate wing flaps whilst flying/swimming.

View file

@ -0,0 +1,30 @@
package com.minelittlepony.api.model;
public interface IUnicorn extends IModel {
/**
* Returns true if this model is being applied to a race that can use magic.
*/
default boolean hasMagic() {
return getRace().hasHorn() && getMagicColor() != 0;
}
/**
* Returns true if this model has an visible horns.
*/
default boolean hasHorn() {
return getRace().hasHorn();
}
/**
* Returns true if this model is currently using magic (horn is lit).
* @return
*/
boolean isCasting();
/**
* Gets the preferred magic color for this mode.
*/
default int getMagicColor() {
return getMetadata().getGlowColor();
}
}

View file

@ -1,7 +1,8 @@
package com.minelittlepony.api.model; package com.minelittlepony.api.model;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.pony.*; import com.minelittlepony.api.pony.*;
import com.minelittlepony.client.*;
import com.minelittlepony.client.pony.PonyData;
import com.minelittlepony.common.util.animation.Interpolator; import com.minelittlepony.common.util.animation.Interpolator;
import com.minelittlepony.util.MathUtil; import com.minelittlepony.util.MathUtil;
@ -9,7 +10,6 @@ import java.util.*;
import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose; import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.*; import net.minecraft.util.*;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -20,11 +20,6 @@ public class ModelAttributes {
* True if the model is sleeping in a bed. * True if the model is sleeping in a bed.
*/ */
public boolean isSleeping; public boolean isSleeping;
/**
* True if the model is lying down comfortably
*/
public boolean isLyingDown;
/** /**
* True if the model is flying like a pegasus. * True if the model is flying like a pegasus.
*/ */
@ -92,7 +87,7 @@ public class ModelAttributes {
* Unique id of the interpolator used for this model. * Unique id of the interpolator used for this model.
* Usually the UUID of the entity being rendered. * Usually the UUID of the entity being rendered.
*/ */
private UUID interpolatorId = UUID.randomUUID(); UUID interpolatorId = UUID.randomUUID();
/** /**
* The actual, visible height of this model when rendered. * The actual, visible height of this model when rendered.
@ -113,7 +108,7 @@ public class ModelAttributes {
/** /**
* Contains the skin metadata associated with this model. * Contains the skin metadata associated with this model.
*/ */
public PonyData metadata = PonyData.NULL; public IPonyData metadata = PonyData.NULL;
public Arm mainArm; public Arm mainArm;
public Hand activeHand; public Hand activeHand;
@ -123,11 +118,11 @@ public class ModelAttributes {
/** /**
* Checks flying and speed conditions and sets rainboom to true if we're a species with wings and is going faaast. * Checks flying and speed conditions and sets rainboom to true if we're a species with wings and is going faaast.
*/ */
public void checkRainboom(LivingEntity entity, PonyModel<?> model, float ticks) { public void checkRainboom(LivingEntity entity, boolean hasWings, float ticks) {
Vec3d motion = entity.getVelocity(); Vec3d motion = entity.getVelocity();
double zMotion = Math.sqrt(motion.x * motion.x + motion.z * motion.z); double zMotion = Math.sqrt(motion.x * motion.x + motion.z * motion.z);
isGoingFast = (isFlying && model instanceof WingedPonyModel) || isGliding; isGoingFast = (isFlying && hasWings) || isGliding;
isGoingFast &= zMotion > 0.4F; isGoingFast &= zMotion > 0.4F;
isGoingFast |= entity.isUsingRiptide(); isGoingFast |= entity.isUsingRiptide();
isGoingFast |= entity.isFallFlying(); isGoingFast |= entity.isFallFlying();
@ -142,33 +137,27 @@ public class ModelAttributes {
return (MathHelper.sin(ticks * 0.136f) / 2) + MathUtil.Angles._270_DEG; return (MathHelper.sin(ticks * 0.136f) / 2) + MathUtil.Angles._270_DEG;
} }
if (isFlying) { if (isFlying) {
return MathHelper.sin(ticks * 0.536f) + WingedPonyModel.WINGS_FULL_SPREAD_ANGLE; return MathHelper.sin(ticks * 0.536f) + IPegasus.WINGS_FULL_SPREAD_ANGLE;
} }
return WingedPonyModel.WINGS_RAISED_ANGLE; return IPegasus.WINGS_RAISED_ANGLE;
} }
public void updateLivingState(LivingEntity entity, Pony pony, Mode mode) { public void updateLivingState(LivingEntity entity, IPony pony, Mode mode) {
visualHeight = entity.getHeight() + 0.125F; visualHeight = entity.getHeight() + 0.125F;
isSitting = PonyPosture.isSitting(entity); isSitting = PonyPosture.isSitting(entity);
isSleeping = entity.isAlive() && entity.isSleeping();; isCrouching = !isSitting && mode == Mode.THIRD_PERSON && PonyPosture.isCrouching(pony, entity);
isLyingDown = isSleeping; isSleeping = entity.isAlive() && entity.isSleeping();
if (entity instanceof PlayerEntity) { isFlying = mode == Mode.THIRD_PERSON && PonyPosture.isFlying(entity);
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.isFallFlying(); isGliding = entity.isFallFlying();
isSwimming = mode == Mode.THIRD_PERSON && PonyPosture.isSwimming(entity); isSwimming = mode == Mode.THIRD_PERSON && PonyPosture.isSwimming(entity);
isSwimmingRotated = isSwimming; isSwimmingRotated = isSwimming;
isRiptide = entity.isUsingRiptide(); isRiptide = entity.isUsingRiptide();
isRidingInteractive = PonyPosture.isRidingAPony(entity); isRidingInteractive = PonyPosture.isRidingAPony(entity);
if (!(entity instanceof PreviewModel)) { if (!(entity instanceof IPreviewModel)) {
interpolatorId = entity.getUuid(); interpolatorId = entity.getUuid();
} }
isLeftHanded = entity.getMainArm() == Arm.LEFT; isLeftHanded = entity.getMainArm() == Arm.LEFT;
isHorsey = PonyConfig.getInstance().horsieMode.get(); isHorsey = MineLittlePony.getInstance().getConfig().horsieMode.get();
featureSkins = SkinsProxy.instance.getAvailableSkins(entity); featureSkins = SkinsProxy.instance.getAvailableSkins(entity);
mainArm = entity.getMainArm(); mainArm = entity.getMainArm();
activeHand = entity.getActiveHand(); activeHand = entity.getActiveHand();
@ -176,7 +165,7 @@ public class ModelAttributes {
} }
public Interpolator getMainInterpolator() { public Interpolator getMainInterpolator() {
return Interpolator.linear(interpolatorId); return metadata.getInterpolator(interpolatorId);
} }
public boolean shouldLiftArm(ArmPose pose, ArmPose complement, float sigma) { public boolean shouldLiftArm(ArmPose pose, ArmPose complement, float sigma) {

View file

@ -1,14 +0,0 @@
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,7 +0,0 @@
package com.minelittlepony.api.model;
public interface PreviewModel {
boolean forceSeapony();
boolean forceNirik();
}

View file

@ -1,9 +1,10 @@
package com.minelittlepony.client.model.armour; package com.minelittlepony.api.model.armour;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.mson.api.ModelKey; import com.minelittlepony.mson.api.ModelKey;
import com.minelittlepony.mson.api.Mson; import com.minelittlepony.mson.api.Mson;
@ -17,7 +18,7 @@ public interface ArmorModelRegistry {
if (id.getNamespace().equals("minecraft")) { if (id.getNamespace().equals("minecraft")) {
return Optional.empty(); return Optional.empty();
} }
return REGISTRY.computeIfAbsent(id.withPath(p -> "armor/" + layer.name().toLowerCase(Locale.ROOT) + "_" + p + ".json"), i -> { return REGISTRY.computeIfAbsent(id.withPath(p -> "models/armor/" + layer.name().toLowerCase(Locale.ROOT) + "_" + p + ".json"), i -> {
return Optional.of(Mson.getInstance().registerModel(i, PonyArmourModel::new)); return Optional.of(Mson.getInstance().registerModel(i, PonyArmourModel::new));
}).filter(key -> key.getModelData().isPresent()); }).filter(key -> key.getModelData().isPresent());
} }

View file

@ -1,4 +1,4 @@
package com.minelittlepony.client.model.armour; package com.minelittlepony.api.model.armour;
/** /**
* The layer used to render a given armour piece. * The layer used to render a given armour piece.

View file

@ -1,6 +1,7 @@
package com.minelittlepony.client.model.armour; package com.minelittlepony.api.model.armour;
import com.minelittlepony.client.model.ModelType; import com.minelittlepony.client.model.ModelType;
import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.mson.api.ModelKey; import com.minelittlepony.mson.api.ModelKey;
import java.util.Optional; import java.util.Optional;

View file

@ -0,0 +1,17 @@
package com.minelittlepony.api.model.armour;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import com.minelittlepony.client.model.IPonyModel;
public interface IArmourModel<T extends LivingEntity> {
/**
* Called to synchronise this armour's angles with that of another.
*
* @param model The other model to mimic
*/
boolean poseModel(T entity, float limbAngle, float limbDistance, float age, float headYaw, float headPitch,
EquipmentSlot slot, ArmourLayer layer,
IPonyModel<T> mainModel);
}

View file

@ -0,0 +1,30 @@
package com.minelittlepony.api.model.armour;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.client.model.armour.DefaultArmourTextureResolver;
/**
* A resolver for looking up the texture for a piece of armour.
* <p>
* This is for modders who want to override the default implementation found in {@link DefaultArmourTextureResolver}.
*/
public interface IArmourTextureResolver {
/**
* Gets the armour texture to be used for the given entity, armour piece, slot, and render layer.
*/
Identifier getTexture(LivingEntity entity, ItemStack itemstack, EquipmentSlot slot, ArmourLayer layer, @Nullable String type);
/**
* Gets the armour variant for the identified texture.
* Either normal for pony-style textures, or legacy for other textures.
*/
default ArmourVariant getVariant(ArmourLayer layer, Identifier resolvedTexture) {
return ArmourVariant.NORMAL;
}
}

View file

@ -1,10 +1,10 @@
package com.minelittlepony.api.events; package com.minelittlepony.api.model.fabric;
import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory; import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import com.minelittlepony.api.model.PonyModel; import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.model.ModelAttributes; import com.minelittlepony.api.model.ModelAttributes;
public interface PonyModelPrepareCallback { public interface PonyModelPrepareCallback {
@ -15,5 +15,5 @@ public interface PonyModelPrepareCallback {
} }
}); });
void onPonyModelPrepared(Entity entity, PonyModel<?> model, ModelAttributes.Mode mode); void onPonyModelPrepared(Entity entity, IModel model, ModelAttributes.Mode mode);
} }

View file

@ -7,25 +7,26 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import com.minelittlepony.api.model.*; import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.model.IPonyModel;
import com.minelittlepony.client.render.entity.feature.GearFeature;
import java.util.*; import java.util.UUID;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* Interface for an accessory on a pony's body. * Interface for an accessory on a pony's body.
*/ */
public interface Gear { public interface IGear {
List<Supplier<Gear>> MOD_GEARS = new ArrayList<>();
/** /**
* Registers a custom gear to be used with the mod. * Registers a custom gear to be used with the mod.
* <p> * <p>
* This would be awesome for creating socks. * This would be awesome for creating socks.
*/ */
static Supplier<Gear> register(Supplier<Gear> gear) { static Supplier<IGear> register(Supplier<IGear> gear) {
MOD_GEARS.add(gear); GearFeature.addModGear(gear);
return gear; return gear;
} }
@ -37,26 +38,13 @@ public interface Gear {
* *
* @return True to render this wearable * @return True to render this wearable
*/ */
boolean canRender(PonyModel<?> model, Entity entity); boolean canRender(IModel model, Entity entity);
/** /**
* Gets the body location that this wearable appears on. * Gets the body location that this wearable appears on.
*/ */
BodyPart getGearLocation(); BodyPart getGearLocation();
default boolean isStackable() {
return false;
}
/**
* The vertical height of this gear when present in a stack.
*
* Any gear rendered after this one will be shifted to sit on top of it.
*/
default float getStackingHeight() {
return 0;
}
/** /**
* Gets the texture to use for this wearable. * Gets the texture to use for this wearable.
* *
@ -74,7 +62,7 @@ public interface Gear {
/** /**
* Applies body transformations for this wearable * Applies body transformations for this wearable
*/ */
default <M extends EntityModel<?> & PonyModel<?>> void transform(M model, MatrixStack matrices) { default <M extends EntityModel<?> & IPonyModel<?>> void transform(M model, MatrixStack matrices) {
BodyPart part = getGearLocation(); BodyPart part = getGearLocation();
model.transform(part, matrices); model.transform(part, matrices);
model.getBodyPart(part).rotate(matrices); model.getBodyPart(part).rotate(matrices);
@ -85,10 +73,23 @@ public interface Gear {
* *
* See {@link AbstractPonyMode.setRotationAndAngle} for an explanation of the various parameters. * See {@link AbstractPonyMode.setRotationAndAngle} for an explanation of the various parameters.
*/ */
default void pose(PonyModel<?> model, Entity entity, boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) { default void pose(IModel model, Entity entity, boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
setModelAttributes(model, entity);
pose(rainboom, interpolatorId, move, swing, bodySwing, ticks);
} }
/**
* @deprecated Use pose(model, entity, rainboom, interpolatorId, move, swing, bodySwing, ticks) instead
*/
@Deprecated(forRemoval = true)
default void setModelAttributes(IModel model, Entity entity) { }
/**
* @deprecated Use pose(model, entity, rainboom, interpolatorId, move, swing, bodySwing, ticks) instead
*/
@Deprecated(forRemoval = true)
default void pose(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) { }
/** /**
* Renders this model component. * Renders this model component.
*/ */
@ -100,7 +101,7 @@ public interface Gear {
* @param <T> The type of entity being rendered. * @param <T> The type of entity being rendered.
* @param <M> The type of the entity's primary model. * @param <M> The type of the entity's primary model.
*/ */
public interface Context<T extends Entity, M extends PonyModel<?>> { public interface Context<T extends Entity, M extends IModel> {
/** /**
* The empty context. * The empty context.
*/ */
@ -109,7 +110,7 @@ public interface Gear {
/** /**
* Checks whether the given wearable and gear are able to render for this specific entity and its renderer. * Checks whether the given wearable and gear are able to render for this specific entity and its renderer.
*/ */
default boolean shouldRender(M model, T entity, Wearable wearable, Gear gear) { default boolean shouldRender(M model, T entity, Wearable wearable, IGear gear) {
return gear.canRender(model, entity); return gear.canRender(model, entity);
} }

View file

@ -0,0 +1,14 @@
package com.minelittlepony.api.model.gear;
/**
* Interface for any gear that changes its position based on where it is in the hat stack.
*/
public interface IStackable {
/**
* The vertical height of this gear when present in a stack.
*
* Any gear rendered after this one will be shifted to sit on top of it.
*/
float getStackingHeight();
}

View file

@ -1,43 +1,20 @@
package com.minelittlepony.api.pony; package com.minelittlepony.api.pony;
import net.minecraft.client.util.DefaultSkinHelper;
import net.minecraft.client.util.SkinTextures;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import com.minelittlepony.api.pony.meta.Race; import java.util.HashMap;
import java.util.Map;
import java.util.*;
public final class DefaultPonySkinHelper { public final class DefaultPonySkinHelper {
public static final Identifier STEVE = new Identifier("minelittlepony", "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 = new Identifier("minelp", "seapony"); private static final Map<Identifier, Identifier> SKINS = new HashMap<>();
public static final Identifier NIRIK_SKIN_TYPE_ID = new Identifier("minelp", "nirik");
private static final Map<SkinTextures, SkinTextures> SKINS = new HashMap<>(); public static Identifier getPonySkin(Identifier original) {
return SKINS.computeIfAbsent(original, DefaultPonySkinHelper::computePonySkin);
public static SkinTextures getTextures(SkinTextures 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) { private static Identifier computePonySkin(Identifier original) {
SkinTextures textures = DefaultSkinHelper.getSkinTextures(id); return new Identifier("minelittlepony", original.getPath().replace(".png", "_pony.png"));
return getModelType(Pony.getManager().getPony(textures.texture(), id).race(), textures.model());
}
public static String getModelType(Race race, SkinTextures.Model armShape) {
if (race.isHuman()) {
return armShape.getName();
}
return (armShape == SkinTextures.Model.SLIM) ? armShape.getName() + race.name().toLowerCase(Locale.ROOT) : race.name().toLowerCase(Locale.ROOT);
} }
} }

View file

@ -0,0 +1,73 @@
package com.minelittlepony.api.pony;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ComparisonChain;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.pony.meta.Race;
public interface IPony extends Comparable<IPony> {
/**
* Gets the global pony manager instance.
*/
static IPonyManager getManager() {
return IPonyManager.Instance.instance;
}
/**
* Gets or creates a new pony associated with the provided resource location.
* The results of this method should not be cached.
*
* @deprecated User IPony.getManager().getPony(texture) instead
*/
@Deprecated
static IPony forResource(Identifier texture) {
return getManager().getPony(texture);
}
/**
* Returns whether this is one of the default ponies assigned to a player without a custom skin.
*/
boolean defaulted();
/**
* Returns whether this pony's metadata block has been initialized.
*/
boolean hasMetadata();
/**
* Gets the race associated with this pony.
*/
default Race race() {
return PonyConfig.getEffectiveRace(metadata().getRace());
}
/**
* Returns true if and only if this metadata represents a pony that can cast magic.
*/
default boolean hasMagic() {
return race().hasHorn() && metadata().getGlowColor() != 0;
}
/**
* Gets the texture used for rendering this pony.
*/
Identifier texture();
/**
* Gets the metadata associated with this pony's model texture.
*/
IPonyData metadata();
@Override
default int compareTo(@Nullable IPony o) {
return o == this ? 0 : o == null ? 1 : ComparisonChain.start()
.compare(texture(), o.texture())
.compare(metadata(), o.metadata())
.result();
}
}

View file

@ -0,0 +1,80 @@
package com.minelittlepony.api.pony;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ComparisonChain;
import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.common.util.animation.Interpolator;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
/**
* Metadata for a pony.
*/
public interface IPonyData extends Comparable<IPonyData> {
/**
* Gets this pony's race.
*
* This is the actual race value. For the effective race, prefer going through {@link IPony#race}
*/
Race getRace();
/**
* Gets the length of the pony's tail.
*/
TailLength getTailLength();
/**
* Gets the shape of the pony's tail.
*/
TailShape getTailShape();
/**
* Get the pony's gender (usually female).
*/
Gender getGender();
/**
* Gets the current pony size.
*/
Size getSize();
/**
* Gets the magical glow colour for magic-casting races. Returns 0 otherwise.
*/
int getGlowColor();
/**
* Returns an array of wearables that this pony is carrying.
*/
Wearable[] getGear();
/**
* Checks it this pony is wearing the given accessory.
*/
boolean isWearing(Wearable wearable);
/**
* Gets an interpolator for interpolating values.
*/
Interpolator getInterpolator(UUID interpolatorId);
/**
* Gets the trigger pixel values as they appeared in the underlying image.
*/
Map<String, TriggerPixelType<?>> getTriggerPixels();
@Override
default int compareTo(@Nullable IPonyData o) {
return o == this ? 0 : o == null ? 1 : ComparisonChain.start()
.compare(getRace(), o.getRace())
.compare(getTailLength(), o.getTailLength())
.compare(getGender(), o.getGender())
.compare(getSize().ordinal(), o.getSize().ordinal())
.compare(getGlowColor(), o.getGlowColor())
.compare(0, Arrays.compare(getGear(), o.getGear()))
.result();
}
}

View file

@ -1,7 +1,6 @@
package com.minelittlepony.api.pony; package com.minelittlepony.api.pony;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@ -11,33 +10,16 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
/** /**
* The PonyManager is responsible for reading and recoding all the pony data associated with the skin of an entity. * The PonyManager is responsible for reading and recoding all the pony data associated with an entity of skin.
*
*/ */
public interface PonyManager { public interface IPonyManager {
/** /**
* Gets a pony representation of the passed in entity. * Gets a pony representation of the passed in entity.
* *
* If the supplied entity is null or can't be determined to be a pony, returns the empty optional. * If the supplied entity is null or can't be determined to be a pony, returns the empty optional.
*/ */
default Optional<Pony> getPony(@Nullable Entity entity) { Optional<IPony> getPony(@Nullable Entity entity);
return entity instanceof LivingEntity living ? getPony(living) : Optional.empty();
}
/**
* Gets a pony representation of the passed in entity.
*
* If the supplied entity is null or can't be determined to be a pony, returns the empty optional.
*/
Optional<Pony> getPony(LivingEntity entity);
/**
* Gets a random background pony determined by the given uuid.
*
* Useful for mods that offer customisation, especially ones that have a whole lot of NPCs.
*
* @param uuid id of a player
*/
Pony getBackgroundPony(UUID uuid);
/** /**
* Gets or creates a pony for the given player. * Gets or creates a pony for the given player.
@ -45,7 +27,14 @@ public interface PonyManager {
* *
* @param player the player * @param player the player
*/ */
Pony getPony(PlayerEntity player); IPony getPony(PlayerEntity player);
/**
* Gets or creates a pony for the given skin resource and vanilla model type.
*
* @param resource A texture resource
*/
IPony getPony(Identifier resource);
/** /**
* Gets or creates a pony for the given skin resource and entity id. * Gets or creates a pony for the given skin resource and entity id.
@ -57,20 +46,25 @@ public interface PonyManager {
* @param resource A texture resource * @param resource A texture resource
* @param uuid id of a player * @param uuid id of a player
*/ */
Pony getPony(@Nullable Identifier resource, @Nullable UUID uuid); IPony getPony(Identifier resource, UUID uuid);
/** /**
* Gets or creates a pony for the given skin resource and vanilla model type. * Gets a random background pony determined by the given uuid.
* *
* @param resource A texture resource * Useful for mods that offer customisation, especially ones that have a whole lot of NPCs.
*
* @param uuid A UUID. Either a user or an entity.
*/ */
default Pony getPony(Identifier resource) { IPony getBackgroundPony(UUID uuid);
return getPony(resource, null);
} /**
* De-registers a pony from the cache.
*/
void removePony(Identifier resource);
interface ForcedPony {} interface ForcedPony {}
final class Instance { final class Instance {
public static PonyManager instance; public static IPonyManager instance;
} }
} }

View file

@ -1,73 +0,0 @@
package com.minelittlepony.api.pony;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ComparisonChain;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.pony.meta.Size;
import java.util.Optional;
import java.util.function.Supplier;
public record Pony (
/**
* Gets the texture used for rendering this pony.
*/
Identifier texture,
Supplier<Optional<PonyData>> metadataGetter
) implements Comparable<Pony> {
/**
* Gets the global pony manager instance.
*/
public static PonyManager getManager() {
return PonyManager.Instance.instance;
}
/**
* Gets the metadata associated with this pony's model texture.
*/
public PonyData metadata() {
return metadataGetter().get().orElse(PonyData.NULL);
}
/**
* Returns whether this pony's metadata block has been initialized.
*/
public boolean hasMetadata() {
return metadataGetter().get().isPresent();
}
public Pony immutableCopy() {
final Optional<PonyData> metadata = metadataGetter().get();
return new Pony(texture(), () -> metadata);
}
/**
* Gets the race associated with this pony.
*/
public Race race() {
return PonyConfig.getEffectiveRace(metadata().race());
}
public Size size() {
return PonyConfig.getEffectiveSize(metadata().size());
}
/**
* Returns true if and only if this metadata represents a pony that can cast magic.
*/
public boolean hasMagic() {
return race().hasHorn() && metadata().glowColor() != 0;
}
@Override
public int compareTo(@Nullable Pony o) {
return o == this ? 0 : o == null ? 1 : ComparisonChain.start()
.compare(texture(), o.texture())
.compare(metadata(), o.metadata())
.result();
}
}

View file

@ -1,116 +0,0 @@
package com.minelittlepony.api.pony;
import net.minecraft.util.Util;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ComparisonChain;
import com.minelittlepony.api.pony.meta.*;
import java.util.*;
import java.util.function.Function;
/**
* Metadata for a pony.
*/
public record PonyData (
/**
* Gets this pony's race.
*
* This is the actual race value. For the effective race, prefer going through {@link Pony#race}
*/
Race race,
/**
* Gets the length of the pony's tail.
*/
TailLength tailLength,
/**
* Gets the shape of the pony's tail.
*/
TailShape tailShape,
/**
* Get the pony's gender (usually female).
*/
Gender gender,
/**
* Gets the current pony size.
*/
Size size,
/**
* Gets the magical glow colour for magic-casting races. Returns 0 otherwise.
*/
int glowColor,
/**
* Returns an array of wearables that this pony is carrying.
*/
Flags<Wearable> gear,
/**
* Indicates whether this pony data corresponds to one of the default/built-in skins
* rather than a user-uploaded one.
*/
boolean noSkin,
/**
* (Experimental) Priority.
* Used to decide which skin to use when dual skin mode is active.
* Provides an optional tie-breaker when the client has to decide between displaying
* either the HDSkins texture or vanilla texture given both are otherwise acceptable.
*
* Any time both skins resolve to the same race (eg. on pony-level HUMANS, or if both are ponies)
* the skin with the highest priority will be chosen.
*
* If both have the same priority, HD Skins' texture will always be used (old default).
*/
int priority,
/**
* Gets the trigger pixel values as they appeared in the underlying image.
*/
Map<String, TValue<?>> attributes
) implements Comparable<PonyData> {
public static final int DEFAULT_MAGIC_COLOR = 0x4444aa;
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 PonyData emptyOf(Race race) {
return OF_RACE.apply(race);
}
public PonyData(TriggerPixel.Mat image, boolean noSkin) {
this(
TriggerPixel.RACE.read(image),
TriggerPixel.TAIL.read(image),
TriggerPixel.TAIL_SHAPE.read(image),
TriggerPixel.GENDER.read(image),
TriggerPixel.SIZE.read(image),
TriggerPixel.GLOW.read(image),
noSkin,
TriggerPixel.PRIORITY.read(image),
TriggerPixel.WEARABLES.read(image)
);
}
public PonyData(Race race, TailLength tailLength, TailShape tailShape, Gender gender, Size size, int glowColor, boolean noSkin, int priority, Flags<Wearable> wearables) {
this(race, tailLength, tailShape, gender, size, glowColor, wearables, noSkin, priority, Util.make(new TreeMap<>(), map -> {
map.put("race", race);
map.put("tailLength", tailLength);
map.put("tailShape", tailShape);
map.put("gender", gender);
map.put("size", size);
map.put("magic", new TValue.Numeric(glowColor));
map.put("priority", new TValue.Numeric(priority));
map.put("gear", wearables);
})
);
}
@Override
public int compareTo(@Nullable PonyData o) {
return o == this ? 0 : o == null ? 1 : ComparisonChain.start()
.compare(race(), o.race())
.compare(tailLength(), o.tailLength())
.compare(gender(), o.gender())
.compare(size().ordinal(), o.size().ordinal())
.compare(glowColor(), o.glowColor())
.compare(0, gear().compareTo(o.gear()))
.result();
}
}

View file

@ -1,27 +1,25 @@
package com.minelittlepony.api.pony; package com.minelittlepony.api.pony;
import com.minelittlepony.api.pony.meta.Race; import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.model.PreviewModel;
import java.util.Optional; import java.util.Optional;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Material;
import net.minecraft.block.StairsBlock; import net.minecraft.block.StairsBlock;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.registry.tag.FluidTags;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public final class PonyPosture { public final class PonyPosture {
public static Optional<Pony> getMountPony(LivingEntity entity) { public static Optional<IPony> getMountPony(LivingEntity entity) {
return entity.getVehicle() instanceof LivingEntity mount return entity.getVehicle() instanceof LivingEntity mount
? Pony.getManager().getPony(mount) ? IPony.getManager().getPony(mount)
: Optional.empty(); : Optional.empty();
} }
public static boolean isCrouching(Pony pony, LivingEntity entity) { public static boolean isCrouching(IPony pony, LivingEntity entity) {
boolean isSneak = entity.isInSneakingPose(); boolean isSneak = entity.isInSneakingPose();
boolean isFlying = isFlying(entity); boolean isFlying = isFlying(entity);
boolean isSwimming = isSwimming(entity); boolean isSwimming = isSwimming(entity);
@ -29,7 +27,7 @@ public final class PonyPosture {
return !isPerformingRainboom(pony, entity) && !isSwimming && isSneak && !isFlying; return !isPerformingRainboom(pony, entity) && !isSwimming && isSneak && !isFlying;
} }
private static boolean isPerformingRainboom(Pony pony, LivingEntity entity) { private static boolean isPerformingRainboom(IPony pony, LivingEntity entity) {
Vec3d motion = entity.getVelocity(); Vec3d motion = entity.getVelocity();
double zMotion = Math.sqrt(motion.x * motion.x + motion.z * motion.z); double zMotion = Math.sqrt(motion.x * motion.x + motion.z * motion.z);
@ -60,7 +58,7 @@ public final class PonyPosture {
double offsetAmount = below.getBlock() instanceof StairsBlock ? 1 : 0.05; double offsetAmount = below.getBlock() instanceof StairsBlock ? 1 : 0.05;
Vec3d pos = entity.getPos(); Vec3d pos = entity.getPos();
BlockPos blockpos = BlockPos.ofFloored( BlockPos blockpos = new BlockPos(
pos.x, pos.x,
pos.y - offsetAmount, pos.y - offsetAmount,
pos.z pos.z
@ -75,7 +73,7 @@ public final class PonyPosture {
public static boolean isPartiallySubmerged(LivingEntity entity) { public static boolean isPartiallySubmerged(LivingEntity entity) {
return entity.isSubmergedInWater() return entity.isSubmergedInWater()
|| entity.getEntityWorld().getBlockState(entity.getBlockPos()).getFluidState().isIn(FluidTags.WATER); || entity.getEntityWorld().getBlockState(entity.getBlockPos()).getMaterial() == Material.WATER;
} }
public static boolean isSitting(LivingEntity entity) { public static boolean isSitting(LivingEntity entity) {
@ -83,43 +81,6 @@ public final class PonyPosture {
} }
public static boolean isRidingAPony(LivingEntity entity) { public static boolean isRidingAPony(LivingEntity entity) {
return isSitting(entity) && getMountPony(entity).map(Pony::race).orElse(Race.HUMAN) != Race.HUMAN; return isSitting(entity) && getMountPony(entity).map(IPony::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.forceSeapony();
}
return Pony.getManager().getPony(entity).filter(pony -> {
return (pony.race() == Race.SEAPONY
|| (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.forceNirik();
}
return Pony.getManager().getPony(entity).filter(pony -> {
return (pony.race() == Race.KIRIN
&& (entity instanceof AbstractClientPlayerEntity player && SkinsProxy.instance.getSkin(DefaultPonySkinHelper.NIRIK_SKIN_TYPE_ID, player).isPresent())
);
}).isPresent();
} }
} }

View file

@ -1,32 +0,0 @@
package com.minelittlepony.api.pony;
import com.mojang.authlib.GameProfile;
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();
public Identifier getSkinTexture(GameProfile profile) {
PlayerSkinProvider skins = MinecraftClient.getInstance().getSkinProvider();
return skins.getSkinTextures(profile).texture();
}
public Optional<Identifier> getSkin(Identifier skinTypeId, AbstractClientPlayerEntity player) {
return Optional.empty();
}
public Set<Identifier> getAvailableSkins(Entity entity) {
return Set.of();
}
}

View file

@ -0,0 +1,24 @@
package com.minelittlepony.api.pony;
import java.util.List;
@SuppressWarnings("unchecked")
public class TriggerPixelSet<T extends Enum<T> & TriggerPixelType<T>> extends TriggerPixelValue<boolean[]> {
private final T def;
public TriggerPixelSet(int color, T def, boolean[] value) {
super(color, value);
this.def = def;
}
@Override
public List<TriggerPixelType<T>> getOptions() {
return def.getOptions();
}
@Override
public boolean matches(Object o) {
return o.getClass() == def.getClass() && getValue()[((Enum<?>)o).ordinal()];
}
}

View file

@ -0,0 +1,74 @@
package com.minelittlepony.api.pony;
import java.util.List;
/**
* Interface for enums that can be parsed from an image trigger pixel value.
*/
public interface TriggerPixelType<T> {
/**
* Gets the pixel colour matching this enum value.
*/
int getColorCode();
/**
* Gets the pixel colour matching this enum value, adjusted to fill all three channels.
*/
default int getChannelAdjustedColorCode() {
return getColorCode();
}
/**
* Gets a string representation of this value.
*/
default String name() {
return "[Numeric " + getHexValue() + "]";
}
default String getHexValue() {
return toHex(getColorCode());
}
/**
* Returns a list of possible values this trigger pixel can accept.
*/
@SuppressWarnings("unchecked")
default <Option extends TriggerPixelType<T>> List<Option> getOptions() {
if (this instanceof Enum) {
// cast is required because gradle's compiler is more strict
return (List<Option>)List.of(getClass().getEnumConstants());
}
return List.of();
}
default boolean matches(Object o) {
return equals(o);
}
/**
* Gets the enum value corresponding to the given enum type and pixel value.
* If none are found, the first parameter is returned as the default.
*
* @param type Return type and default value.
* @param pixelValue The pixel colour to search for.
*/
@SuppressWarnings("unchecked")
static <T extends TriggerPixelType<T>> T getByTriggerPixel(T type, int pixelValue) {
return (T)type.getOptions().stream()
.filter(i -> i.getColorCode() == pixelValue)
.findFirst()
.orElse(type);
}
static TriggerPixelType<?> of(int color) {
return () -> color;
}
static String toHex(int color) {
String v = Integer.toHexString(color).toUpperCase();
while (v.length() < 6) {
v = "0" + v;
}
return "#" + v;
}
}

View file

@ -0,0 +1,43 @@
package com.minelittlepony.api.pony;
import java.util.List;
import java.util.Objects;
public class TriggerPixelValue<T> implements TriggerPixelType<T> {
private final int color;
private final T value;
public TriggerPixelValue(int color, T value) {
this.color = color;
this.value = value;
}
@SuppressWarnings("unchecked")
@Override
public String name() {
return value instanceof TriggerPixelType ? ((TriggerPixelType<T>)value).name() : TriggerPixelType.super.name();
}
@SuppressWarnings("unchecked")
@Override
public <Option extends TriggerPixelType<T>> List<Option> getOptions() {
return value instanceof TriggerPixelType ? ((TriggerPixelType<T>)value).getOptions() : TriggerPixelType.super.getOptions();
}
public T getValue() {
return value;
}
@Override
public int getColorCode() {
return color;
}
@Override
public boolean equals(Object o) {
return super.equals(o)
|| (o instanceof TriggerPixelValue && Objects.equals(((TriggerPixelValue<?>)o).getValue(), getValue()))
|| Objects.equals(o, getValue());
}
}

View file

@ -1,59 +0,0 @@
package com.minelittlepony.api.pony.meta;
import net.minecraft.network.PacketByteBuf;
import java.util.*;
public record Flags<T extends Enum<T> & TValue<T>> (
T def,
Set<T> values,
int colorCode
) implements Comparable<Flags<T>>, TValue<T> {
public static <T extends Enum<T> & TValue<T>> Flags<T> of(T def) {
return new Flags<>(def, Set.<T>of(), 0);
}
public static <T extends Enum<T> & TValue<T>> Flags<T> of(T def, int colorCode, Set<T> values) {
return new Flags<>(def, values, colorCode);
}
public static <T extends Enum<T> & TValue<T>> Flags<T> read(T def, PacketByteBuf buffer) {
int length = buffer.readVarInt();
@SuppressWarnings("unchecked")
Set<T> values = EnumSet.noneOf((Class<T>)def.getClass());
@SuppressWarnings("unchecked")
T[] all = (T[])def.getClass().getEnumConstants();
for (int i = 0; i < length; i++) {
values.add(all[buffer.readInt()]);
}
return new Flags<>(def, values, buffer.readInt());
}
public void write(PacketByteBuf buffer) {
buffer.writeCollection(values, (buf, value) -> buf.writeInt(value.ordinal()));
buffer.writeInt(colorCode);
}
@Override
public String name() {
return "[Flags " + values + "]";
}
@Override
public List<TValue<T>> getOptions() {
return def.getOptions();
}
@SuppressWarnings("unchecked")
@Override
public boolean matches(TValue<?> o) {
return o.getClass() == def.getClass() && values.contains((T)o);
}
@Override
public int compareTo(Flags<T> other) {
return Integer.compare(colorCode(), other.colorCode());
}
}

View file

@ -1,6 +1,8 @@
package com.minelittlepony.api.pony.meta; package com.minelittlepony.api.pony.meta;
public enum Gender implements TValue<Gender> { import com.minelittlepony.api.pony.TriggerPixelType;
public enum Gender implements TriggerPixelType<Gender> {
MARE(0), MARE(0),
STALLION(0xffffff), STALLION(0xffffff),
ABOMONATION(0x888888); ABOMONATION(0x888888);
@ -12,7 +14,7 @@ public enum Gender implements TValue<Gender> {
} }
@Override @Override
public int colorCode() { public int getColorCode() {
return triggerValue; return triggerValue;
} }

View file

@ -1,6 +1,11 @@
package com.minelittlepony.api.pony.meta; package com.minelittlepony.api.pony.meta;
public enum Race implements TValue<Race> { import com.minelittlepony.api.pony.TriggerPixelType;
import java.util.Arrays;
import java.util.List;
public enum Race implements TriggerPixelType<Race> {
HUMAN (0x000000, false, false), HUMAN (0x000000, false, false),
EARTH (0xf9b131, false, false), EARTH (0xf9b131, false, false),
PEGASUS (0x88caf0, true, false), PEGASUS (0x88caf0, true, false),
@ -15,13 +20,16 @@ public enum Race implements TValue<Race> {
BATPONY (0xeeeeee, true, false), BATPONY (0xeeeeee, true, false),
SEAPONY (0x3655dd, false, true); SEAPONY (0x3655dd, false, true);
private final boolean wings; private boolean wings;
private final boolean horn; private boolean horn;
private final int colorCode; private int triggerPixel;
public static final List<Race> REGISTRY = Arrays.asList(values());
Race(int triggerPixel, boolean wings, boolean horn) {
this.triggerPixel = triggerPixel;
Race(int colorCode, boolean wings, boolean horn) {
this.colorCode = colorCode;
this.wings = wings; this.wings = wings;
this.horn = horn; this.horn = horn;
} }
@ -62,7 +70,14 @@ public enum Race implements TValue<Race> {
} }
@Override @Override
public int colorCode() { public int getColorCode() {
return colorCode; return triggerPixel;
}
public String getModelId(boolean isSlim) {
if (isHuman()) {
return isSlim ? "slim" : "default";
}
return isSlim ? "slim" + name().toLowerCase() : name().toLowerCase();
} }
} }

View file

@ -1,15 +1,17 @@
package com.minelittlepony.api.pony.meta; package com.minelittlepony.api.pony.meta;
import com.minelittlepony.api.pony.TriggerPixelType;
/** /**
* Represents the different model sizes that are possible. * Represents the different model sizes that are possible.
* *
* For a list of possible presets, look at {@link SizePreset}. * For a list of possible presets, look at {@link Sizes}.
* This interface exists for servers so they can work with this information even though they might not have access to the client config. * This interface exists for servers so they can work with this information even though they might not have access to the client config.
* *
*/ */
public interface Size extends TValue<Size> { public interface Size extends TriggerPixelType<Size> {
/** /**
* The Enum index of this size. May be used on the client to convert to an instance of Sizes or use {@link SizePreset#of} * The Enum index of this size. May be used on the client to convert to an instance of Sizes or use {@link Sizes#of}
* *
* Made to be compatible with the enum variant. * Made to be compatible with the enum variant.
*/ */
@ -25,25 +27,20 @@ public interface Size extends TValue<Size> {
/** /**
* A scale factor that controls the size of the shadow that appears under the entity. * A scale factor that controls the size of the shadow that appears under the entity.
*/ */
float shadowSize(); float getShadowSize();
/** /**
* The global scale factor applied to all physical dimensions. * The global scale factor applied to all physical dimensions.
*/ */
float scaleFactor(); float getScaleFactor();
/** /**
* A scale factor used to alter the vertical eye position. * A scale factor used to alter the vertical eye position.
*/ */
float eyeHeightFactor(); float getEyeHeightFactor();
/** /**
* A scale factor used to alter the camera's distance. * A scale factor used to alter the camera's distance.
*/ */
float eyeDistanceFactor(); float getEyeDistanceFactor();
/**
* The trigger pixel colour corresponding to this size.
*/
int colorCode();
} }

View file

@ -9,7 +9,7 @@ import com.minelittlepony.api.config.PonyConfig;
* *
* For spooky things at a distance, use {@link Size} instead. * For spooky things at a distance, use {@link Size} instead.
*/ */
public enum SizePreset implements Size { public enum Sizes implements Size {
TALL (0x534b76, 0.45f, 1.1F, 1.15F), TALL (0x534b76, 0.45f, 1.1F, 1.15F),
BULKY (0xce3254, 0.5f, 1, 1.05F), BULKY (0xce3254, 0.5f, 1, 1.05F),
LANKY (0x3254ce, 0.45F, 0.85F, 0.9F), LANKY (0x3254ce, 0.45F, 0.85F, 0.9F),
@ -18,12 +18,15 @@ public enum SizePreset implements Size {
FOAL (0xffbe53, 0.25f, 0.6F, 0.5F), FOAL (0xffbe53, 0.25f, 0.6F, 0.5F),
UNSET (0x000000, 1, 1, 1); UNSET (0x000000, 1, 1, 1);
private final int triggerValue; public static final Sizes[] REGISTRY = values();
private final float shadowSize;
private final float scale;
private final float camera;
SizePreset(int pixel, float shadowSz, float scaleF, float cameraF) { private int triggerValue;
private float shadowSize;
private float scale;
private float camera;
Sizes(int pixel, float shadowSz, float scaleF, float cameraF) {
triggerValue = pixel; triggerValue = pixel;
shadowSize = shadowSz; shadowSize = shadowSz;
scale = scaleF; scale = scaleF;
@ -31,22 +34,17 @@ public enum SizePreset implements Size {
} }
@Override @Override
public int colorCode() { public float getShadowSize() {
return triggerValue;
}
@Override
public float shadowSize() {
return shadowSize * PonyConfig.getInstance().getGlobalScaleFactor(); return shadowSize * PonyConfig.getInstance().getGlobalScaleFactor();
} }
@Override @Override
public float scaleFactor() { public float getScaleFactor() {
return scale * PonyConfig.getInstance().getGlobalScaleFactor(); return scale * PonyConfig.getInstance().getGlobalScaleFactor();
} }
@Override @Override
public float eyeHeightFactor() { public float getEyeHeightFactor() {
if (!PonyConfig.getInstance().fillycam.get()) { if (!PonyConfig.getInstance().fillycam.get()) {
return 1; return 1;
} }
@ -54,7 +52,26 @@ public enum SizePreset implements Size {
} }
@Override @Override
public float eyeDistanceFactor() { public float getEyeDistanceFactor() {
return eyeHeightFactor(); if (!PonyConfig.getInstance().fillycam.get()) {
return 1;
}
return camera * PonyConfig.getInstance().getGlobalScaleFactor();
}
@Override
public int getColorCode() {
return triggerValue;
}
public static Sizes of(Size size) {
if (size instanceof Sizes) {
return (Sizes)size;
}
int i = size.ordinal();
if (i < 0 || i >= REGISTRY.length) {
return UNSET;
}
return REGISTRY[i];
} }
} }

View file

@ -1,66 +0,0 @@
package com.minelittlepony.api.pony.meta;
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> {
/**
* Gets the pixel colour matching this enum value.
*/
int colorCode();
/**
* Gets the pixel colour matching this enum value, adjusted to fill all three channels.
*/
default int getChannelAdjustedColorCode() {
return colorCode();
}
/**
* Gets a string representation of this value.
*/
String name();
default String getHexValue() {
return toHex(colorCode());
}
/**
* Returns a list of possible values this trigger pixel can accept.
*/
@SuppressWarnings("unchecked")
default List<TValue<T>> getOptions() {
if (this instanceof Enum) {
// cast is required because gradle's compiler is more strict
return Arrays.asList(getClass().getEnumConstants());
}
return List.of();
}
default boolean matches(TValue<?> o) {
return o != null && colorCode() == o.colorCode();
}
static String toHex(int color) {
String v = Integer.toHexString(color).toUpperCase();
while (v.length() < 6) {
v = "0" + v;
}
return "#" + v;
}
public record Numeric(int colorCode) implements TValue<Integer> {
@Override
public String name() {
return "[Numeric " + getHexValue() + "]";
}
@Override
public List<TValue<Integer>> getOptions() {
return List.of();
}
}
}

View file

@ -1,6 +1,8 @@
package com.minelittlepony.api.pony.meta; package com.minelittlepony.api.pony.meta;
public enum TailLength implements TValue<TailLength> { import com.minelittlepony.api.pony.TriggerPixelType;
public enum TailLength implements TriggerPixelType<TailLength> {
STUB (0x425844), STUB (0x425844),
QUARTER (0xd19fe4), QUARTER (0xd19fe4),
HALF (0x534b76), HALF (0x534b76),
@ -14,7 +16,7 @@ public enum TailLength implements TValue<TailLength> {
} }
@Override @Override
public int colorCode() { public int getColorCode() {
return triggerValue; return triggerValue;
} }
} }

View file

@ -1,6 +1,8 @@
package com.minelittlepony.api.pony.meta; package com.minelittlepony.api.pony.meta;
public enum TailShape implements TValue<TailShape> { import com.minelittlepony.api.pony.TriggerPixelType;
public enum TailShape implements TriggerPixelType<TailShape> {
STRAIGHT(0), STRAIGHT(0),
BUMPY (0xfc539f), BUMPY (0xfc539f),
SWIRLY (0x3eff22), SWIRLY (0x3eff22),
@ -13,7 +15,7 @@ public enum TailShape implements TValue<TailShape> {
} }
@Override @Override
public int colorCode() { public int getColorCode() {
return triggerValue; return triggerValue;
} }
} }

View file

@ -1,92 +1,115 @@
package com.minelittlepony.api.pony.meta; package com.minelittlepony.api.pony.meta;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.client.texture.NativeImage;
import net.minecraft.util.math.ColorHelper;
import org.joml.Vector2i;
import com.minelittlepony.api.pony.TriggerPixelSet;
import com.minelittlepony.api.pony.TriggerPixelType;
import com.minelittlepony.api.pony.TriggerPixelValue;
import com.minelittlepony.common.util.Color; import com.minelittlepony.common.util.Color;
import java.util.*; import java.util.Arrays;
/** /**
* Individual trigger pixels for a pony skin. * Individual trigger pixels for a pony skin.
*
*/ */
public interface TriggerPixel<T> { @SuppressWarnings("unchecked")
Vector2i MAX_COORDS = new Vector2i(); public enum TriggerPixel {
RACE(Race.HUMAN, Channel.ALL, 0, 0),
TAIL(TailLength.FULL, Channel.ALL, 1, 0),
GENDER(Gender.MARE, Channel.ALL, 2, 0),
SIZE(Sizes.NORMAL, Channel.ALL, 3, 0),
GLOW(null, Channel.RAW, 0, 1),
WEARABLES(Wearable.NONE, Channel.RAW, 1, 1),
TAIL_SHAPE(TailShape.STRAIGHT, Channel.ALL, 2, 1);
TriggerPixel<Race> RACE = ofOptions(0, 0, Race.HUMAN, Race.values()); private int x;
TriggerPixel<TailLength> TAIL = ofOptions(1, 0, TailLength.FULL, TailLength.values()); private int y;
TriggerPixel<Gender> GENDER = ofOptions(2, 0, Gender.MARE, Gender.values());
TriggerPixel<TailShape> TAIL_SHAPE = ofOptions(2, 1, TailShape.STRAIGHT, TailShape.values());
TriggerPixel<Size> SIZE = ofOptions(3, 0, SizePreset.NORMAL, SizePreset.values());
TriggerPixel<Integer> GLOW = ofColor(0, 1);
TriggerPixel<Flags<Wearable>> WEARABLES = ofFlags(1, 1, Wearable.EMPTY_FLAGS, Wearable.values());
TriggerPixel<Integer> PRIORITY = ofColor(2, 2);
static <T extends TValue<T>> TriggerPixel<T> ofOptions(int x, int y, T def, T[] options) { private Channel channel;
MAX_COORDS.x = Math.max(MAX_COORDS.x, x);
MAX_COORDS.y = Math.max(MAX_COORDS.y, y);
Int2ObjectOpenHashMap<T> lookup = buildLookup(options);
return image -> {
int color = Color.abgrToArgb(image.getColor(x, y));
if (ColorHelper.Argb.getAlpha(color) < 255) { TriggerPixelType<?> def;
return (T)def;
} private static final TriggerPixel[] VALUES = values();
return lookup.getOrDefault(color & 0x00FFFFFF, def); private static final int MAX_READ_X = Arrays.stream(VALUES).mapToInt(i -> i.x).max().getAsInt();
}; private static final int MAX_READ_Y = Arrays.stream(VALUES).mapToInt(i -> i.y).max().getAsInt();
TriggerPixel(TriggerPixelType<?> def, Channel channel, int x, int y) {
this.def = def;
this.channel = channel;
this.x = x;
this.y = y;
} }
static TriggerPixel<Integer> ofColor(int x, int y) { /**
MAX_COORDS.x = Math.max(MAX_COORDS.x, x); * Reads this trigger pixel's value and returns the raw colour.
MAX_COORDS.y = Math.max(MAX_COORDS.y, y); *
return image -> Color.abgrToArgb(image.getColor(x, y)); * @param image Image to read
*/
public int readColor(NativeImage image) {
return channel.readValue(x, y, image);
} }
static <T extends Enum<T> & TValue<T>> TriggerPixel<Flags<T>> ofFlags(int x, int y, Flags<T> def, T[] options) { /**
MAX_COORDS.x = Math.max(MAX_COORDS.x, x); * Reads this trigger pixel's value and parses it to an Enum instance.
MAX_COORDS.y = Math.max(MAX_COORDS.y, y); *
Int2ObjectOpenHashMap<T> lookup = buildLookup(options); * @param image Image to read
var flagReader = new Object() { */
boolean readFlag(int color, Set<T> values) { public <T extends TriggerPixelType<T>> TriggerPixelValue<T> readValue(NativeImage image) {
T value = lookup.get(color); int color = readColor(image);
return value != null && values.add(value);
}
};
return image -> {
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.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;
};
}
static <T extends TValue<T>> Int2ObjectOpenHashMap<T> buildLookup(T[] options) { if (Channel.ALPHA.readValue(x, y, image) < 255) {
Int2ObjectOpenHashMap<T> lookup = new Int2ObjectOpenHashMap<>(); return new TriggerPixelValue<>(color, (T)def);
for (int i = 0; i < options.length; i++) {
lookup.put(options[i].colorCode(), options[i]);
} }
return lookup;
return new TriggerPixelValue<>(color, TriggerPixelType.getByTriggerPixel((T)def, color));
} }
public <T extends Enum<T> & TriggerPixelType<T>> TriggerPixelSet<T> readFlags(NativeImage image) {
T read(Mat image); boolean[] out = new boolean[def.getClass().getEnumConstants().length];
readFlags(out, image);
static boolean isTriggerPixelCoord(int x, int y) { return new TriggerPixelSet<>(readColor(image), (T)def, out);
return x <= MAX_COORDS.x && y <= MAX_COORDS.y;
} }
interface Mat { public <T extends Enum<T> & TriggerPixelType<T>> void readFlags(boolean[] out, NativeImage image) {
int getColor(int x, int y); readFlag(out, Channel.RED, image);
readFlag(out, Channel.GREEN, image);
readFlag(out, Channel.BLUE, image);
} }
private <T extends Enum<T> & TriggerPixelType<T>> void readFlag(boolean[] out, Channel channel, NativeImage image) {
if (Channel.ALPHA.readValue(x, y, image) < 255) {
return;
}
T value = TriggerPixelType.getByTriggerPixel((T)def, channel.readValue(x, y, image));
out[value.ordinal()] |= value != def;
}
public static boolean isTriggerPixelCoord(int x, int y) {
return x <= MAX_READ_X && y <= MAX_READ_Y;
}
enum Channel {
RAW (0xFFFFFFFF, 0),
ALL (0x00FFFFFF, 0),
ALPHA(0x000000FF, 24),
RED (0x000000FF, 0),
GREEN(0x000000FF, 8),
BLUE (0x000000FF, 16);
private int mask;
private int offset;
Channel(int mask, int offset) {
this.mask = mask;
this.offset = offset;
}
public int readValue(int x, int y, NativeImage image) {
return (Color.abgrToArgb(image.getColor(x, y)) >> offset) & mask;
}
}
} }

View file

@ -1,31 +1,34 @@
package com.minelittlepony.api.pony.meta; package com.minelittlepony.api.pony.meta;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.ColorHelper;
import com.minelittlepony.api.pony.TriggerPixelType;
import com.minelittlepony.client.model.gear.SaddleBags;
import com.minelittlepony.common.util.Color;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public enum Wearable implements TValue<Wearable> { public enum Wearable implements TriggerPixelType<Wearable> {
NONE (0x00, null), NONE (0x00, null),
CROWN (0x16, new Identifier("minelittlepony", "textures/models/crown.png")), CROWN (0x16, new Identifier("minelittlepony", "textures/models/crown.png")),
MUFFIN (0x32, new Identifier("minelittlepony", "textures/models/muffin.png")), MUFFIN (0x32, new Identifier("minelittlepony", "textures/models/muffin.png")),
HAT (0x64, new Identifier("textures/entity/witch.png")), HAT (0x64, new Identifier("textures/entity/witch.png")),
ANTLERS (0x96, new Identifier("minelittlepony", "textures/models/antlers.png")), ANTLERS (0x96, new Identifier("minelittlepony", "textures/models/antlers.png")),
SADDLE_BAGS_LEFT (0xC6, new Identifier("minelittlepony", "textures/models/saddlebags.png")), SADDLE_BAGS_LEFT (0xC6, SaddleBags.TEXTURE),
SADDLE_BAGS_RIGHT (0xC7, new Identifier("minelittlepony", "textures/models/saddlebags.png")), SADDLE_BAGS_RIGHT (0xC7, SaddleBags.TEXTURE),
SADDLE_BAGS_BOTH (0xC8, new Identifier("minelittlepony", "textures/models/saddlebags.png")), SADDLE_BAGS_BOTH (0xC8, SaddleBags.TEXTURE),
STETSON (0xFA, new Identifier("minelittlepony", "textures/models/stetson.png")); STETSON (0xFA, new Identifier("minelittlepony", "textures/models/stetson.png"));
private int triggerValue; private int triggerValue;
private final Identifier id; private final Identifier id;
private final Identifier texture; private final Identifier texture;
public static final Map<Identifier, Wearable> REGISTRY = Arrays.stream(values()).collect(Collectors.toMap(Wearable::getId, Function.identity())); public static final List<Wearable> VALUES = Arrays.stream(values()).toList();
public static final Map<Identifier, Wearable> REGISTRY = VALUES.stream().collect(Collectors.toMap(Wearable::getId, Function.identity()));
public static final Flags<Wearable> EMPTY_FLAGS = Flags.of(NONE);
Wearable(int pixel, Identifier texture) { Wearable(int pixel, Identifier texture) {
triggerValue = pixel; triggerValue = pixel;
@ -42,7 +45,7 @@ public enum Wearable implements TValue<Wearable> {
} }
@Override @Override
public int colorCode() { public int getColorCode() {
return triggerValue; return triggerValue;
} }
@ -52,6 +55,22 @@ public enum Wearable implements TValue<Wearable> {
@Override @Override
public int getChannelAdjustedColorCode() { public int getChannelAdjustedColorCode() {
return triggerValue == 0 ? 0 : ColorHelper.Argb.getArgb(255, triggerValue, triggerValue, triggerValue); return triggerValue == 0 ? 0 : Color.argbToHex(255, triggerValue, triggerValue, triggerValue);
}
public static boolean[] flags(Wearable[] wears) {
boolean[] flags = new boolean[VALUES.size()];
for (int i = 0; i < wears.length; i++) {
flags[wears[i].ordinal()] = true;
}
return flags;
}
public static Wearable[] flags(boolean[] flags) {
List<Wearable> wears = new ArrayList<>();
for (int i = 0; i < VALUES.size(); i++) {
if (flags[i]) wears.add(VALUES.get(i));
}
return wears.toArray(new Wearable[0]);
} }
} }

View file

@ -0,0 +1,255 @@
package com.minelittlepony.api.pony.network;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.Util;
import com.google.common.base.MoreObjects;
import com.google.common.base.Suppliers;
import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.api.pony.TriggerPixelType;
import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.common.util.animation.Interpolator;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
import java.util.function.Supplier;
public class MsgPonyData implements IPonyData {
private static final short API_IDENTIFIER = (short) 0xABCD;
// API version - increment this number before any time any data is added/removed/moved in the data stream
private static final byte API_VERSION = 2;
private final Race race;
private final TailLength tailLength;
private final TailShape tailShape;
private final Gender gender;
private final Size size;
private final int glowColor;
private final boolean noSkin;
private final int wearableColor;
private final boolean[] wearables;
private final Supplier<Map<String, TriggerPixelType<?>>> triggerPixels = Suppliers.memoize(() -> Util.make(new TreeMap<>(), this::initTriggerPixels));
private void initTriggerPixels(Map<String, TriggerPixelType<?>> map) {
map.put("race", race);
map.put("tailLength", tailLength);
map.put("tailShape", tailShape);
map.put("gender", gender);
map.put("size", size);
map.put("magic", TriggerPixelType.of(glowColor));
map.put("gear", TriggerPixelType.of(wearableColor));
}
public MsgPonyData(PacketByteBuf buffer) {
short data = buffer.readShort();
if (data != API_IDENTIFIER || buffer.readByte() != API_VERSION) {
race = null;
tailLength = null;
tailShape = null;
gender = null;
size = null;
glowColor = 0;
noSkin = true;
wearables = null;
wearableColor = 0;
return;
}
race = buffer.readEnumConstant(Race.class);
tailLength = buffer.readEnumConstant(TailLength.class);
tailShape = buffer.readEnumConstant(TailShape.class);
gender = buffer.readEnumConstant(Gender.class);
size = new MsgSize(buffer);
glowColor = buffer.readInt();
noSkin = buffer.readBoolean();
Wearable[] gear = new Wearable[buffer.readInt()];
Wearable[] all = Wearable.values();
for (int i = 0; i < gear.length; i++) {
gear[i] = all[buffer.readInt()];
}
wearables = Wearable.flags(gear);
wearableColor = buffer.readInt();
}
public MsgPonyData(IPonyData data, boolean noSkin) {
race = data.getRace();
tailLength = data.getTailLength();
tailShape = data.getTailShape();
gender = data.getGender();
size = data.getSize();
glowColor = data.getGlowColor();
wearables = Wearable.flags(data.getGear());
wearableColor = data.getTriggerPixels().get("gear").getColorCode();
this.noSkin = noSkin;
}
public PacketByteBuf toBuffer(PacketByteBuf buffer) {
buffer.writeShort(API_IDENTIFIER);
buffer.writeByte(API_VERSION);
buffer.writeEnumConstant(race);
buffer.writeEnumConstant(tailLength);
buffer.writeEnumConstant(tailShape);
buffer.writeEnumConstant(gender);
new MsgSize(size).toBuffer(buffer);
buffer.writeInt(glowColor);
buffer.writeBoolean(noSkin);
Wearable[] gear = getGear();
buffer.writeInt(gear.length);
for (int i = 0; i < gear.length; i++) {
buffer.writeInt(gear[i].ordinal());
}
buffer.writeInt(wearableColor);
return buffer;
}
public boolean isNoSkin() {
return noSkin;
}
@Override
public Race getRace() {
return race;
}
@Override
public TailLength getTailLength() {
return tailLength;
}
@Override
public TailShape getTailShape() {
return tailShape;
}
@Override
public Gender getGender() {
return gender;
}
@Override
public Size getSize() {
return size;
}
@Override
public int getGlowColor() {
return glowColor;
}
@Override
public Wearable[] getGear() {
return Wearable.flags(wearables);
}
@Override
public boolean isWearing(Wearable wearable) {
return wearables[wearable.ordinal()];
}
@Override
public Interpolator getInterpolator(UUID interpolatorId) {
return Interpolator.linear(interpolatorId);
}
@Override
public Map<String, TriggerPixelType<?>> getTriggerPixels() {
return triggerPixels.get();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("race", race)
.add("tailLength", tailLength)
.add("tailShape", tailShape)
.add("gender", gender)
.add("size", size)
.add("wearables", getGear())
.add("glowColor", TriggerPixelType.toHex(glowColor))
.toString();
}
private static final class MsgSize implements Size {
private final int ordinal;
private final String name;
private final float shadow;
private final float scale;
private final float eyeHeight;
private final float eyeDistance;
private final int triggerPixel;
MsgSize(Size size) {
ordinal = size.ordinal();
name = size.name();
shadow = size.getShadowSize();
scale = size.getScaleFactor();
eyeHeight = size.getEyeHeightFactor();
eyeDistance = size.getEyeDistanceFactor();
triggerPixel = size.getColorCode();
}
MsgSize(PacketByteBuf buffer) {
ordinal = buffer.readInt();
name = buffer.readString(32767);
shadow = buffer.readFloat();
scale = buffer.readFloat();
eyeHeight = buffer.readFloat();
eyeDistance = buffer.readFloat();
triggerPixel = buffer.readInt();
}
public void toBuffer(PacketByteBuf buffer) {
buffer.writeInt(ordinal);
buffer.writeString(name);
buffer.writeFloat(shadow);
buffer.writeFloat(scale);
buffer.writeFloat(eyeHeight);
buffer.writeFloat(eyeDistance);
buffer.writeFloat(triggerPixel);
}
@Override
public int ordinal() {
return ordinal;
}
@Override
public String name() {
return name;
}
@Override
public float getShadowSize() {
return shadow;
}
@Override
public float getScaleFactor() {
return scale;
}
@Override
public float getEyeHeightFactor() {
return eyeHeight;
}
@Override
public float getEyeDistanceFactor() {
return eyeDistance;
}
@Override
public String toString() {
return name;
}
@Override
public int getColorCode() {
return triggerPixel;
}
}
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.api.events; package com.minelittlepony.api.pony.network.fabric;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
@ -8,56 +8,62 @@ import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager; import com.minelittlepony.api.pony.IPony;
import org.apache.logging.log4j.Logger; import com.minelittlepony.api.pony.network.MsgPonyData;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.api.pony.PonyData;
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
public class Channel { public class Channel {
private static final Identifier CLIENT_PONY_DATA = new Identifier("minelittlepony", "pony_data"); 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 Identifier REQUEST_PONY_DATA = new Identifier("minelittlepony", "request_pony_data");
private static final Logger LOGGER = LogManager.getLogger("MineLittlePony:Networking");
private static boolean registered; private static boolean registered;
public static void bootstrap() { public static void bootstrap() {
ClientLoginConnectionEvents.INIT.register((handler, client) -> { ClientLoginConnectionEvents.INIT.register((handler, client) -> {
registered = false; registered = false;
MineLittlePony.logger.info("Resetting registered flag");
}); });
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
LOGGER.info("Sending consent packet to " + handler.getPlayer().getName().getString()); MineLittlePony.logger.info("Sending consent packet to " + handler.getPlayer().getName().getString());
sender.sendPacket(REQUEST_PONY_DATA, PacketByteBufs.empty()); sender.sendPacket(REQUEST_PONY_DATA, PacketByteBufs.empty());
}); });
ClientPlayNetworking.registerGlobalReceiver(REQUEST_PONY_DATA, (client, handler, ignored, sender) -> { ClientPlayNetworking.registerGlobalReceiver(REQUEST_PONY_DATA, (client, handler, ignored, sender) -> {
registered = true; if (client.player != null) {
LOGGER.info("Server has just consented"); IPony pony = IPony.getManager().getPony(client.player);
registered = true;
MineLittlePony.logger.info("Server has just consented");
sender.sendPacket(CLIENT_PONY_DATA, new MsgPonyData(pony.metadata(), pony.defaulted()).toBuffer(PacketByteBufs.create()));
}
}); });
ServerPlayNetworking.registerGlobalReceiver(CLIENT_PONY_DATA, (server, player, ignore, buffer, ignore2) -> { ServerPlayNetworking.registerGlobalReceiver(CLIENT_PONY_DATA, (server, player, ignore, buffer, ignore2) -> {
PonyData packet = MsgPonyData.read(buffer); MsgPonyData packet = new MsgPonyData(buffer);
server.execute(() -> { server.execute(() -> {
PonyDataCallback.EVENT.invoker().onPonyDataAvailable(player, packet, EnvType.SERVER); PonyDataCallback.EVENT.invoker().onPonyDataAvailable(player, packet, packet.isNoSkin(), EnvType.SERVER);
}); });
}); });
} }
public static boolean isRegistered() { public static void broadcastPonyData(MsgPonyData packet) {
return registered;
}
public static boolean broadcastPonyData(PonyData packet) {
if (!isRegistered()) {
return false;
}
if (FabricLoader.getInstance().getEnvironmentType() != EnvType.CLIENT) { if (FabricLoader.getInstance().getEnvironmentType() != EnvType.CLIENT) {
throw new RuntimeException("Client packet send called by the server"); throw new RuntimeException("Client packet send called by the server");
} }
ClientPlayNetworking.send(CLIENT_PONY_DATA, MsgPonyData.write(packet, PacketByteBufs.create())); if (!registered) {
return true; if (MinecraftClient.getInstance().isInSingleplayer() || MinecraftClient.getInstance().isIntegratedServerRunning()) {
MineLittlePony.logger.info("Sending pony skin data over as we are either in single-player or lan");
} else {
MineLittlePony.logger.info("Skipping network packet as the server has not consented");
return;
}
}
ClientPlayNetworking.send(CLIENT_PONY_DATA, packet.toBuffer(PacketByteBufs.create()));
} }
} }

View file

@ -1,20 +1,23 @@
package com.minelittlepony.api.events; package com.minelittlepony.api.pony.network.fabric;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory; import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import com.minelittlepony.api.pony.PonyData; import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.client.MineLittlePony;
/** /**
* Callback triggered on the server when receiving pony data from a client. * Callback triggered on the server when receiving pony data from a client.
* *
*/ */
public interface PonyDataCallback { public interface PonyDataCallback {
Event<PonyDataCallback> EVENT = EventFactory.createArrayBacked(PonyDataCallback.class, listeners -> (sender, data, env) -> {
Event<PonyDataCallback> EVENT = EventFactory.createArrayBacked(PonyDataCallback.class, listeners -> (sender, data, noSkin, env) -> {
MineLittlePony.logger.info("Got pony data on the " + env + " from " + sender.getUuidAsString() + " with " + (noSkin ? "un" : "") + "set skin and he is a " + data.getRace() + "!");
for (PonyDataCallback event : listeners) { for (PonyDataCallback event : listeners) {
event.onPonyDataAvailable(sender, data, env); event.onPonyDataAvailable(sender, data, noSkin, env);
} }
}); });
@ -22,7 +25,8 @@ public interface PonyDataCallback {
* Called when pony data is received. * Called when pony data is received.
* @param sender The player who sent the data - this is the owner of the skin/pony data. * @param sender The player who sent the data - this is the owner of the skin/pony data.
* @param data The skin/pony data * @param data The skin/pony data
* @param noSkin Whether the data is for a player with a default/unset custom skin.
* @param env The environment. Whether this call is coming from the client or server. Clients may get two calls, one for both. * @param env The environment. Whether this call is coming from the client or server. Clients may get two calls, one for both.
*/ */
void onPonyDataAvailable(PlayerEntity sender, PonyData data, EnvType env); void onPonyDataAvailable(PlayerEntity sender, IPonyData data, boolean noSkin, EnvType env);
} }

View file

@ -1,12 +1,13 @@
package com.minelittlepony.client; package com.minelittlepony.client;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.*; import net.minecraft.text.*;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.client.render.MobRenderers; import com.minelittlepony.client.render.MobRenderers;
import com.minelittlepony.client.render.PonyRenderDispatcher;
import com.minelittlepony.client.settings.ClientPonyConfig;
import com.minelittlepony.common.client.gui.GameGui; import com.minelittlepony.common.client.gui.GameGui;
import com.minelittlepony.common.client.gui.ScrollContainer; import com.minelittlepony.common.client.gui.ScrollContainer;
import com.minelittlepony.common.client.gui.Tooltip; import com.minelittlepony.common.client.gui.Tooltip;
@ -24,7 +25,7 @@ import org.jetbrains.annotations.Nullable;
* In-Game options menu. * In-Game options menu.
* *
*/ */
public class PonySettingsScreen extends GameGui { public class GuiPonySettings extends GameGui {
private static final String OPTIONS_PREFIX = "minelp.options."; private static final String OPTIONS_PREFIX = "minelp.options.";
private static final String PONY_LEVEL = OPTIONS_PREFIX + "ponylevel"; private static final String PONY_LEVEL = OPTIONS_PREFIX + "ponylevel";
private static final String MOB_PREFIX = "minelp.mobs."; private static final String MOB_PREFIX = "minelp.mobs.";
@ -35,22 +36,17 @@ public class PonySettingsScreen extends GameGui {
public static final Text SCALE_SHOW = Text.translatable("minelp.debug.scale.sa"); public static final Text SCALE_SHOW = Text.translatable("minelp.debug.scale.sa");
public static final Text SCALE_MIN = Text.translatable("minelp.debug.scale.min"); public static final Text SCALE_MIN = Text.translatable("minelp.debug.scale.min");
public static HorseButtonFactory buttonFactory = (screen, parent, row, RIGHT, content) -> { private ClientPonyConfig config;
content.addButton(new Button(RIGHT, row += 20, 150, 20))
.setEnabled(false)
.getStyle()
.setTooltip(Tooltip.of("minelp.options.skins.hdskins.disabled", 200))
.setText("minelp.options.skins.hdskins.open");
};
private final PonyConfig config = PonyConfig.getInstance();
private final ScrollContainer content = new ScrollContainer(); private final ScrollContainer content = new ScrollContainer();
private final boolean hiddenOptions; private final boolean hiddenOptions;
public PonySettingsScreen(@Nullable Screen parent) { public GuiPonySettings(@Nullable Screen parent) {
super(Text.literal(OPTIONS_PREFIX + "title"), parent); super(Text.literal(OPTIONS_PREFIX + "title"), parent);
config = (ClientPonyConfig)MineLittlePony.getInstance().getConfig();
content.margin.top = 30; content.margin.top = 30;
content.margin.bottom = 30; content.margin.bottom = 30;
content.getContentPadding().top = 10; content.getContentPadding().top = 10;
@ -122,7 +118,7 @@ public class PonySettingsScreen extends GameGui {
.onChange(i == config.horsieMode ? (v -> { .onChange(i == config.horsieMode ? (v -> {
v = ((Setting<Boolean>)i).set(v); v = ((Setting<Boolean>)i).set(v);
MineLittlePony.getInstance().getRenderDispatcher().initialise(MinecraftClient.getInstance().getEntityRenderDispatcher(), true); PonyRenderDispatcher.getInstance().initialise(MinecraftClient.getInstance().getEntityRenderDispatcher(), true);
return v; return v;
}) : (Setting<Boolean>)i) }) : (Setting<Boolean>)i)
.setEnabled(enabled); .setEnabled(enabled);
@ -164,7 +160,7 @@ public class PonySettingsScreen extends GameGui {
row += 15; row += 15;
content.addButton(new Label(RIGHT, row)).getStyle().setText("minelp.options.skins"); content.addButton(new Label(RIGHT, row)).getStyle().setText("minelp.options.skins");
buttonFactory.renderOption(this, parent, row, RIGHT, content); SkinsProxy.instance.renderOption(this, parent, row, RIGHT, content);
} }
public Text describeCurrentScale(AbstractSlider<Float> sender) { public Text describeCurrentScale(AbstractSlider<Float> sender) {
@ -193,17 +189,14 @@ public class PonySettingsScreen extends GameGui {
} }
@Override @Override
public void render(DrawContext context, int mouseX, int mouseY, float tickDelta) { public void render(MatrixStack matrices, int mouseX, int mouseY, float partialTicks) {
super.render(context, mouseX, mouseY, tickDelta); renderBackground(matrices);
content.render(context, mouseX, mouseY, tickDelta); super.render(matrices, mouseX, mouseY, partialTicks);
content.render(matrices, mouseX, mouseY, partialTicks);
} }
@Override @Override
public void removed() { public void removed() {
config.save(); config.save();
} }
public interface HorseButtonFactory {
void renderOption(Screen screen, @Nullable Screen parent, int row, int RIGHT, ScrollContainer content);
}
} }

View file

@ -5,8 +5,7 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.hit.HitResult; import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.*; import net.minecraft.util.math.*;
import com.minelittlepony.api.config.PonyConfig; import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.common.util.settings.Setting; import com.minelittlepony.common.util.settings.Setting;
public class HorseCam { public class HorseCam {
@ -32,7 +31,7 @@ public class HorseCam {
*/ */
public static float transformCameraAngle(float pitch) { public static float transformCameraAngle(float pitch) {
if (!PonyConfig.getInstance().fillycam.get()) { if (!MineLittlePony.getInstance().getConfig().fillycam.get()) {
return pitch; return pitch;
} }
@ -49,10 +48,10 @@ public class HorseCam {
return pitch; return pitch;
} }
Pony pony = Pony.getManager().getPony(player); IPony pony = IPony.getManager().getPony(player);
if (!pony.race().isHuman()) { if (!pony.race().isHuman()) {
Setting<Boolean> fillyCam = PonyConfig.getInstance().fillycam; Setting<Boolean> fillyCam = MineLittlePony.getInstance().getConfig().fillycam;
fillyCam.set(false); fillyCam.set(false);
final float vanillaHeight = player.getEyeHeight(player.getPose()); final float vanillaHeight = player.getEyeHeight(player.getPose());

View file

@ -0,0 +1,5 @@
package com.minelittlepony.client;
public interface IPreviewModel {
}

View file

@ -13,6 +13,7 @@ import static com.minelittlepony.common.event.SkinFilterCallback.copy;
* *
*/ */
class LegacySkinConverter implements SkinFilterCallback { class LegacySkinConverter implements SkinFilterCallback {
@Override @Override
public void processImage(NativeImage image, boolean legacy) { public void processImage(NativeImage image, boolean legacy) {
if (legacy) { if (legacy) {

View file

@ -1,11 +1,12 @@
package com.minelittlepony.client; package com.minelittlepony.client;
import com.minelittlepony.api.config.PonyConfig; import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.events.Channel; import com.minelittlepony.api.pony.network.fabric.Channel;
import com.minelittlepony.client.model.ModelType; import com.minelittlepony.client.model.ModelType;
import com.minelittlepony.client.model.armour.ArmourTextureResolver; import com.minelittlepony.client.pony.PonyManager;
import com.minelittlepony.client.render.MobRenderers; import com.minelittlepony.client.pony.VariatedTextureSupplier;
import com.minelittlepony.client.render.PonyRenderDispatcher; import com.minelittlepony.client.render.PonyRenderDispatcher;
import com.minelittlepony.client.settings.ClientPonyConfig;
import com.minelittlepony.common.client.gui.VisibilityMode; import com.minelittlepony.common.client.gui.VisibilityMode;
import com.minelittlepony.common.client.gui.element.Button; import com.minelittlepony.common.client.gui.element.Button;
import com.minelittlepony.common.client.gui.sprite.TextureSprite; import com.minelittlepony.common.client.gui.sprite.TextureSprite;
@ -14,8 +15,6 @@ import com.minelittlepony.common.event.ScreenInitCallback;
import com.minelittlepony.common.event.SkinFilterCallback; import com.minelittlepony.common.event.SkinFilterCallback;
import com.minelittlepony.common.util.GamePaths; import com.minelittlepony.common.util.GamePaths;
import java.nio.file.Path;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
@ -26,7 +25,6 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.TitleScreen; import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.option.KeyBinding; import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.InputUtil;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.resource.ResourceType; import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -42,13 +40,14 @@ public class MineLittlePony implements ClientModInitializer {
public static final Logger logger = LogManager.getLogger("MineLittlePony"); public static final Logger logger = LogManager.getLogger("MineLittlePony");
private PonyManagerImpl ponyManager; private final PonyRenderDispatcher renderManager = PonyRenderDispatcher.getInstance();
private ClientPonyConfig config;
private PonyManager ponyManager;
private VariatedTextureSupplier variatedTextures; private VariatedTextureSupplier variatedTextures;
private final KeyBinding keyBinding = new KeyBinding("key.minelittlepony.settings", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_F9, "key.categories.misc"); 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 boolean hasHdSkins; private boolean hasHdSkins;
private boolean hasModMenu; private boolean hasModMenu;
@ -68,8 +67,8 @@ public class MineLittlePony implements ClientModInitializer {
hasHdSkins = FabricLoader.getInstance().isModLoaded("hdskins"); hasHdSkins = FabricLoader.getInstance().isModLoaded("hdskins");
hasModMenu = FabricLoader.getInstance().isModLoaded("modmenu"); hasModMenu = FabricLoader.getInstance().isModLoaded("modmenu");
PonyConfig config = new ClientPonyConfig(GamePaths.getConfigDirectory().resolve("minelp.json")); config = new ClientPonyConfig(GamePaths.getConfigDirectory().resolve("minelp.json"));
ponyManager = new PonyManagerImpl(config); ponyManager = new PonyManager(config);
variatedTextures = new VariatedTextureSupplier(); variatedTextures = new VariatedTextureSupplier();
KeyBindingHelper.registerKeyBinding(keyBinding); KeyBindingHelper.registerKeyBinding(keyBinding);
@ -83,9 +82,7 @@ public class MineLittlePony implements ClientModInitializer {
// general events // general events
ClientReadyCallback.Handler.register(); ClientReadyCallback.Handler.register();
ClientTickEvents.END_CLIENT_TICK.register(this::onTick); ClientTickEvents.END_CLIENT_TICK.register(this::onTick);
ClientReadyCallback.EVENT.register(client -> { ClientReadyCallback.EVENT.register(this::onClientReady);
renderDispatcher.initialise(client.getEntityRenderDispatcher(), false);
});
ScreenInitCallback.EVENT.register(this::onScreenInit); ScreenInitCallback.EVENT.register(this::onScreenInit);
config.load(); config.load();
@ -96,6 +93,10 @@ public class MineLittlePony implements ClientModInitializer {
FabricLoader.getInstance().getEntrypoints("minelittlepony", ClientModInitializer.class).forEach(ClientModInitializer::onInitializeClient); FabricLoader.getInstance().getEntrypoints("minelittlepony", ClientModInitializer.class).forEach(ClientModInitializer::onInitializeClient);
} }
private void onClientReady(MinecraftClient client) {
renderManager.initialise(client.getEntityRenderDispatcher(), false);
}
private void onTick(MinecraftClient client) { private void onTick(MinecraftClient client) {
boolean inGame = client.world != null && client.player != null && client.currentScreen == null; boolean inGame = client.world != null && client.player != null && client.currentScreen == null;
@ -106,13 +107,13 @@ public class MineLittlePony implements ClientModInitializer {
} }
if ((mainMenu || inGame) && keyBinding.isPressed()) { if ((mainMenu || inGame) && keyBinding.isPressed()) {
client.setScreen(new PonySettingsScreen(client.currentScreen)); client.setScreen(new GuiPonySettings(client.currentScreen));
} }
} }
private void onScreenInit(Screen screen, ScreenInitCallback.ButtonList buttons) { private void onScreenInit(Screen screen, ScreenInitCallback.ButtonList buttons) {
if (screen instanceof TitleScreen) { if (screen instanceof TitleScreen) {
VisibilityMode mode = ClientPonyConfig.getInstance().horseButton.get(); VisibilityMode mode = config.horseButton.get();
boolean show = mode == VisibilityMode.ON || (mode == VisibilityMode.AUTO boolean show = mode == VisibilityMode.ON || (mode == VisibilityMode.AUTO
&& !(hasHdSkins || hasModMenu && !(hasHdSkins || hasModMenu
)); ));
@ -120,7 +121,7 @@ public class MineLittlePony implements ClientModInitializer {
if (show) { if (show) {
int y = hasHdSkins ? 75 : 50; int y = hasHdSkins ? 75 : 50;
Button button = buttons.addButton(new Button(screen.width - 50, screen.height - y, 20, 20)) Button button = buttons.addButton(new Button(screen.width - 50, screen.height - y, 20, 20))
.onClick(sender -> MinecraftClient.getInstance().setScreen(new PonySettingsScreen(screen))); .onClick(sender -> MinecraftClient.getInstance().setScreen(new GuiPonySettings(screen)));
button.getStyle() button.getStyle()
.setIcon(new TextureSprite() .setIcon(new TextureSprite()
.setPosition(2, 2) .setPosition(2, 2)
@ -133,36 +134,19 @@ public class MineLittlePony implements ClientModInitializer {
} }
} }
public PonyManagerImpl getManager() { /**
* Gets the global MineLP client configuration.
*/
public PonyConfig getConfig() {
return config;
}
public PonyManager getManager() {
return ponyManager; return ponyManager;
} }
public VariatedTextureSupplier getVariatedTextures() { public VariatedTextureSupplier getVariatedTextures() {
return variatedTextures; return variatedTextures;
} }
/**
* Gets the static pony render manager responsible for all entity renderers.
*/
public PonyRenderDispatcher getRenderDispatcher() {
return renderDispatcher;
}
private static final class ClientPonyConfig extends PonyConfig {
public ClientPonyConfig(Path path) {
super(path);
MobRenderers.REGISTRY.values().forEach(r -> value("entities", r.name, true));
disablePonifiedArmour.onChanged(t -> ArmourTextureResolver.INSTANCE.invalidate());
}
@Override
public void save() {
super.save();
PlayerEntity player = MinecraftClient.getInstance().player;
if (player != null) {
player.calculateDimensions();
}
}
}
} }

View file

@ -1,6 +1,6 @@
package com.minelittlepony.client; package com.minelittlepony.client;
import com.minelittlepony.api.pony.Pony; import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.PonyPosture; import com.minelittlepony.api.pony.PonyPosture;
import com.minelittlepony.client.transform.PonyTransformation; import com.minelittlepony.client.transform.PonyTransformation;
@ -16,8 +16,8 @@ public class PonyBounds {
return PonyPosture.getMountPony(entity).map(ridingPony -> { return PonyPosture.getMountPony(entity).map(ridingPony -> {
LivingEntity vehicle = (LivingEntity)entity.getVehicle(); LivingEntity vehicle = (LivingEntity)entity.getVehicle();
Vec3d offset = PonyTransformation.forSize(ridingPony.size()).getRiderOffset(); Vec3d offset = PonyTransformation.forSize(ridingPony.metadata().getSize()).getRiderOffset();
float scale = ridingPony.metadata().size().scaleFactor(); float scale = ridingPony.metadata().getSize().getScaleFactor();
return getAbsoluteRidingOffset(vehicle).add( return getAbsoluteRidingOffset(vehicle).add(
0, 0,
@ -31,7 +31,7 @@ public class PonyBounds {
float delta = MinecraftClient.getInstance().getTickDelta(); float delta = MinecraftClient.getInstance().getTickDelta();
Entity vehicle = entity.getVehicle(); Entity vehicle = entity.getVehicle();
double vehicleOffset = vehicle == null ? 0 : vehicle.getHeight(); double vehicleOffset = vehicle == null ? 0 : vehicle.getHeight() - vehicle.getMountedHeightOffset();
return new Vec3d( return new Vec3d(
MathHelper.lerp(delta, entity.prevX, entity.getX()), MathHelper.lerp(delta, entity.prevX, entity.getX()),
@ -40,8 +40,8 @@ public class PonyBounds {
); );
} }
public static Box getBoundingBox(Pony pony, LivingEntity entity) { public static Box getBoundingBox(IPony pony, LivingEntity entity) {
final float scale = pony.size().scaleFactor() + 0.1F; final float scale = pony.metadata().getSize().getScaleFactor() + 0.1F;
final float width = entity.getWidth() * scale; final float width = entity.getWidth() * scale;
final float height = entity.getHeight() * scale; final float height = entity.getHeight() * scale;

View file

@ -1,87 +0,0 @@
package com.minelittlepony.client;
import net.minecraft.client.MinecraftClient;
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;
import java.io.IOException;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
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.
* This may either come from an attached json file or the image itself.
*/
public static Supplier<Optional<PonyData>> parse(@Nullable Identifier identifier, boolean noSkin) {
if (identifier == null) {
return NULL;
}
return MinecraftClient.getInstance().getResourceManager().getResource(identifier).flatMap(res -> {
try {
return res.getMetadata().decode(SERIALIZER);
} catch (IOException e) {
MineLittlePony.logger.warn("Unable to read {} metadata", identifier, e);
}
return Optional.empty();
}).map(PonyDataLoader::loaded).orElseGet(() -> {
return load(callback -> {
NativeUtil.parseImage(identifier, image -> {
callback.accept(new PonyData(image, noSkin));
}, e -> {
MineLittlePony.logger.fatal("Unable to read {} metadata", identifier, e);
callback.accept(PonyData.NULL);
});
});
});
}
private static <T> Supplier<Optional<T>> loaded(T t) {
final Optional<T> value = Optional.of(t);
return () -> value;
}
private static <T> Supplier<Optional<T>> load(Consumer<Consumer<T>> factory) {
return new Supplier<Optional<T>>() {
Optional<T> value = Optional.empty();
boolean loadRequested;
@Override
public Optional<T> get() {
synchronized (this) {
if (!loadRequested) {
loadRequested = true;
factory.accept(value -> {
this.value = Optional.ofNullable(value);
});
}
}
return value;
}
};
}
}

View file

@ -1,123 +0,0 @@
package com.minelittlepony.client;
import com.google.common.cache.*;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.config.PonyLevel;
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.util.DefaultSkinHelper;
import net.minecraft.resource.ResourceManager;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
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 = new Identifier("minelittlepony", "background_ponies");
private final PonyConfig config;
private final LoadingCache<Identifier, Pony> defaultedPoniesCache = CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.SECONDS)
.build(CacheLoader.from(resource -> new Pony(resource, PonyDataLoader.parse(resource, true))));
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;
Instance.instance = this;
}
private Pony loadPony(Identifier resource, boolean defaulted) {
try {
return (defaulted ? defaultedPoniesCache : poniesCache).get(resource);
} catch (ExecutionException e) {
return new Pony(resource, PonyDataLoader.NULL);
}
}
@Override
public Pony getPony(PlayerEntity player) {
return getPony(getSkin(player), player instanceof ForcedPony ? null : player.getGameProfile() == null ? player.getUuid() : player.getGameProfile().getId());
}
@Override
public Optional<Pony> getPony(LivingEntity entity) {
if (entity instanceof PlayerEntity player) {
return Optional.of(getPony(player));
}
Identifier skin = getSkin(entity);
return skin == null ? Optional.empty() : Optional.of(getPony(skin, null));
}
@Override
public Pony getPony(@Nullable Identifier resource, @Nullable UUID uuid) {
if (resource == null) {
return uuid == null ? loadPony(DefaultSkinHelper.getTexture(), true) : getBackgroundPony(uuid);
}
Pony pony = loadPony(resource, false);
if (uuid != null && PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES && pony.metadata().race().isHuman()) {
return getBackgroundPony(uuid);
}
return pony;
}
@Override
public Pony getBackgroundPony(@Nullable UUID uuid) {
if (config.ponyLevel.get() == PonyLevel.PONIES) {
return loadPony(MineLittlePony.getInstance().getVariatedTextures().get(VariatedTextureSupplier.BACKGROUND_PONIES_POOL, uuid).orElse(DefaultSkinHelper.getSkinTextures(uuid).texture()), true);
}
return loadPony(DefaultSkinHelper.getSkinTextures(uuid).texture(), true);
}
@Nullable
private Identifier getSkin(LivingEntity entity) {
if (entity instanceof PlayerEntity player) {
if (player.getGameProfile() != null && player instanceof AbstractClientPlayerEntity clientPlayer) {
return clientPlayer.getSkinTextures().texture();
}
} else {
if (MineLittlePony.getInstance().getRenderDispatcher().getPonyRenderer(entity) != null) {
return MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity).getTexture(entity);
}
}
return null;
}
public void removePony(Identifier resource) {
poniesCache.invalidate(resource);
defaultedPoniesCache.invalidate(resource);
}
public void clearCache() {
MineLittlePony.logger.info("Flushed {} cached ponies.", poniesCache.size());
poniesCache.invalidateAll();
defaultedPoniesCache.invalidateAll();
}
@Override
public void reload(ResourceManager var1) {
clearCache();
PonySkullRenderer.reload();
}
@Override
public Identifier getFabricId() {
return ID;
}
}

View file

@ -0,0 +1,68 @@
package com.minelittlepony.client;
import com.minelittlepony.common.client.gui.ScrollContainer;
import com.minelittlepony.common.client.gui.Tooltip;
import com.minelittlepony.common.client.gui.element.Button;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import java.util.Optional;
import java.util.Set;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.texture.PlayerSkinProvider;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
public class SkinsProxy {
public static SkinsProxy instance = new SkinsProxy();
@Nullable
public Identifier getSkinTexture(GameProfile profile) {
PlayerSkinProvider skins = MinecraftClient.getInstance().getSkinProvider();
@Nullable
MinecraftProfileTexture texture = skins.getTextures(profile).get(MinecraftProfileTexture.Type.SKIN);
if (texture == null) {
return null;
}
return skins.loadSkin(texture, MinecraftProfileTexture.Type.SKIN);
}
public void renderOption(Screen screen, @Nullable Screen parent, int row, int RIGHT, ScrollContainer content) {
content.addButton(new Button(RIGHT, row += 20, 150, 20))
.setEnabled(false)
.getStyle()
.setTooltip(Tooltip.of("minelp.options.skins.hdskins.disabled", 200))
.setText("minelp.options.skins.hdskins.open");
}
public Optional<Identifier> getSkin(Identifier skinTypeId, AbstractClientPlayerEntity player) {
return Optional.empty();
}
public Set<Identifier> getAvailableSkins(Entity entity) {
return Set.of();
}
}

View file

@ -1,31 +0,0 @@
package com.minelittlepony.client.compat.hdskins;
import net.minecraft.client.world.ClientWorld;
import com.minelittlepony.api.model.PreviewModel;
import com.minelittlepony.api.pony.*;
import com.minelittlepony.hdskins.client.gui.player.*;
import com.minelittlepony.hdskins.client.gui.player.skins.PlayerSkins;
import java.util.UUID;
/**
* Dummy model used for the skin uploading screen.
*/
class DummyPony extends DummyPlayer implements PreviewModel, PonyManager.ForcedPony {
public DummyPony(ClientWorld world, PlayerSkins<?> textures) {
super(world, textures);
setUuid(UUID.randomUUID()); // uuid must be random so animations aren't linked between the two previews
}
@Override
public boolean forceSeapony() {
return getTextures().getPosture().getActiveSkinType() == MineLPHDSkins.seaponySkinType;
}
@Override
public boolean forceNirik() {
return getTextures().getPosture().getActiveSkinType() == MineLPHDSkins.nirikSkinType;
}
}

View file

@ -1,71 +0,0 @@
package com.minelittlepony.client.compat.hdskins;
import com.minelittlepony.client.PonySettingsScreen;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.common.client.gui.dimension.Bounds;
import com.minelittlepony.common.client.gui.element.Button;
import com.minelittlepony.common.client.gui.sprite.TextureSprite;
import com.minelittlepony.hdskins.client.gui.DualCarouselWidget;
import com.minelittlepony.hdskins.client.gui.GuiSkins;
import com.minelittlepony.hdskins.server.SkinServerList;
import com.minelittlepony.hdskins.profile.SkinType;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.util.Identifier;
/**
* Skin uploading GUI. Usually displayed over the main menu.
*/
class GuiSkinsMineLP extends GuiSkins {
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");
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");
if (type == SkinType.SKIN) {
MineLittlePony.getInstance().getManager().removePony(location);
}
});
}
@Override
protected void initServerPreviewButtons(Bounds area) {
if (!(parent instanceof PonySettingsScreen)) {
addButton(new Button(area.right() - 20, area.bottom() + 5, 20, 20))
.onClick(sender -> client.setScreen(new PonySettingsScreen(this)))
.getStyle()
.setIcon(new TextureSprite()
.setPosition(2, 2)
.setTexture(new Identifier("minelittlepony", "textures/gui/pony.png"))
.setTextureSize(16, 16)
.setSize(16, 16))
.setTooltip("minelp.options.title", 0, 10);
super.initServerPreviewButtons(new Bounds(area.top, area.left, area.width - 25, area.height));
} else {
super.initServerPreviewButtons(area);
}
}
@Override
public DualCarouselWidget createPreviewer() {
return new PonifiedDualCarouselWidget(this);
}
@Override
protected Identifier getBackground() {
int i = (int)Math.floor(Math.random() * PANORAMAS.length);
return new Identifier(PANORAMAS[i]);
}
}

View file

@ -1,70 +0,0 @@
package com.minelittlepony.client.compat.hdskins;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.text.Text;
import com.minelittlepony.api.pony.*;
import com.minelittlepony.api.pony.meta.TValue;
import com.minelittlepony.common.client.gui.ITextContext;
import com.minelittlepony.common.client.gui.dimension.Bounds;
import com.minelittlepony.hdskins.client.gui.Carousel;
import com.minelittlepony.hdskins.client.gui.player.DummyPlayer;
import java.util.List;
import java.util.stream.Collectors;
class LegendOverlayWidget implements Carousel.Element, ITextContext {
private static final Bounds LEGEND_BLOCK_BOUNDS = new Bounds(0, 0, 10, 10);
private final Bounds frame;
public LegendOverlayWidget(Bounds frame) {
this.frame = frame;
}
@Override
public void render(DummyPlayer player, DrawContext context, int mouseX, int mouseY) {
PonyData data = Pony.getManager().getPony(player).metadata();
int[] index = new int[1];
data.attributes().forEach((key, value) -> {
context.getMatrices().push();
int i = index[0]++;
int x = frame.left;
int y = frame.top + (i * 10 + 20);
context.getMatrices().translate(x, y, 1);
drawLegendBlock(context, 0, 0, 0, mouseX - x, mouseY - y, key, value);
context.getMatrices().pop();
});
}
private void drawLegendBlock(DrawContext context, int index, int x, int y, int mouseX, int mouseY, String key, TValue<?> value) {
context.fill(0, 0, LEGEND_BLOCK_BOUNDS.width, LEGEND_BLOCK_BOUNDS.height, 0xFF003333);
context.fill(1, 1, LEGEND_BLOCK_BOUNDS.width - 1, LEGEND_BLOCK_BOUNDS.height - 1, value.colorCode() | 0xFF000000);
char symbol = value.name().charAt(0);
if (symbol == '[') {
symbol = key.charAt(0);
}
context.drawTextWithShadow(getFont(), Text.literal(String.valueOf(symbol).toUpperCase()), 2, 1, 0xFFFFFFFF);
if (LEGEND_BLOCK_BOUNDS.contains(mouseX, mouseY)) {
List<Text> lines = value.getOptions().stream().map(option -> {
boolean selected = value.matches(option);
return Text.literal((selected ? "* " : " ") + option.name()).styled(s -> {
int color = option.getChannelAdjustedColorCode();
return (color == 0 ? s : s.withColor(color)).withItalic(selected);
});
}).collect(Collectors.toList());
lines.add(0, Text.of(key.toUpperCase() + ": " + value.getHexValue()));
if (lines.size() == 1) {
lines.add(Text.literal(value.name()).styled(s -> {
int color = value.getChannelAdjustedColorCode();
return color == 0 ? s : s.withColor(value.colorCode());
}));
}
context.drawTooltip(getFont(), lines, 2, 10);
}
}
}

View file

@ -1,52 +0,0 @@
package com.minelittlepony.client.compat.hdskins;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.Identifier;
import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.client.render.entity.SeaponyRenderer;
import com.minelittlepony.hdskins.client.gui.DualCarouselWidget;
import com.minelittlepony.hdskins.client.gui.GuiSkins;
import com.minelittlepony.hdskins.client.gui.player.DummyPlayer;
import com.minelittlepony.hdskins.client.gui.player.skins.PlayerSkins;
import com.minelittlepony.hdskins.client.resources.NativeImageFilters;
import com.minelittlepony.hdskins.client.resources.TextureLoader;
import com.minelittlepony.hdskins.profile.SkinType;
class PonifiedDualCarouselWidget extends DualCarouselWidget {
public PonifiedDualCarouselWidget(GuiSkins screen) {
super(screen);
local.addElement(new LegendOverlayWidget(local.bounds));
remote.addElement(new LegendOverlayWidget(remote.bounds));
}
@Override
protected DummyPlayer createEntity(ClientWorld world, PlayerSkins<?> textures) {
return new DummyPony(world, textures);
}
@Override
public Identifier getDefaultSkin(SkinType type, String modelVariant) {
if (type == MineLPHDSkins.seaponySkinType) {
return NativeImageFilters.GREYSCALE.load(SeaponyRenderer.SEAPONY, SeaponyRenderer.SEAPONY, getExclusion());
}
if (type == MineLPHDSkins.nirikSkinType) {
return super.getDefaultSkin(SkinType.SKIN, modelVariant);
}
Wearable wearable = MineLPHDSkins.wearableTypes.getOrDefault(type, Wearable.NONE);
if (wearable != Wearable.NONE) {
return NativeImageFilters.GREYSCALE.load(wearable.getDefaultTexture(), wearable.getDefaultTexture(), getExclusion());
}
return super.getDefaultSkin(type, modelVariant);
}
@Override
public TextureLoader.Exclusion getExclusion() {
return TriggerPixel::isTriggerPixelCoord;
}
}

View file

@ -0,0 +1,44 @@
package com.minelittlepony.client.hdskins;
import net.minecraft.client.world.ClientWorld;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.IPonyManager;
import com.minelittlepony.client.IPreviewModel;
import com.minelittlepony.client.render.EquineRenderManager;
import com.minelittlepony.hdskins.client.dummy.*;
import java.util.UUID;
/**
* Dummy model used for the skin uploading screen.
*/
class DummyPony extends DummyPlayer implements IPreviewModel, IPonyManager.ForcedPony, EquineRenderManager.RegistrationHandler {
public DummyPony(ClientWorld world, PlayerSkins<?> textures) {
super(world, textures);
setUuid(UUID.randomUUID()); // uuid must be random so animations aren't linked between the two previews
}
@Override
public boolean shouldUpdateRegistration(IPony pony) {
return false;
}
@Override
public boolean isSubmergedInWater() {
return getTextures().getPosture().getActiveSkinType() == MineLPHDSkins.seaponySkinType || super.isSubmergedInWater();
}
@Override
public String getModel() {
if (getTextures().getPosture().getActiveSkinType() == MineLPHDSkins.seaponySkinType) {
return getTextures().usesThinSkin() ? "slimseapony" : "seapony";
}
return IPony.getManager()
.getPony(this)
.metadata()
.getRace()
.getModelId(getTextures().usesThinSkin());
}
}

View file

@ -0,0 +1,91 @@
package com.minelittlepony.client.hdskins;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.client.GuiPonySettings;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.common.client.gui.element.Button;
import com.minelittlepony.common.client.gui.sprite.TextureSprite;
import com.minelittlepony.hdskins.client.dummy.PlayerPreview;
import com.minelittlepony.hdskins.client.gui.GuiSkins;
import com.minelittlepony.hdskins.server.SkinServerList;
import com.minelittlepony.hdskins.profile.SkinType;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.util.Identifier;
import org.lwjgl.glfw.GLFW;
/**
* Skin uploading GUI. Usually displayed over the main menu.
*/
class GuiSkinsMineLP extends GuiSkins {
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);
}
@Override
public void init() {
super.init();
if (!(parent instanceof GuiPonySettings)) {
addButton(new Button(width - 25, height - 90, 20, 20))
.onClick(sender -> client.setScreen(new GuiPonySettings(this)))
.getStyle()
.setIcon(new TextureSprite()
.setPosition(2, 2)
.setTexture(new Identifier("minelittlepony", "textures/gui/pony.png"))
.setTextureSize(16, 16)
.setSize(16, 16))
.setTooltip("minelp.options.title", 0, 10);
}
}
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (modifiers == (GLFW.GLFW_MOD_ALT | GLFW.GLFW_MOD_CONTROL) && keyCode == GLFW.GLFW_KEY_R) {
client.reloadResources();
return true;
}
return super.keyPressed(keyCode, scanCode, modifiers);
}
@Override
public PlayerPreview createPreviewer() {
return new PonyPreview();
}
@Override
protected Identifier getBackground() {
int i = (int)Math.floor(Math.random() * PANORAMAS.length);
return new Identifier(PANORAMAS[i]);
}
@Override
public void onSetLocalSkin(SkinType type) {
super.onSetLocalSkin(type);
MineLittlePony.logger.debug("Invalidating old local skin, checking updated local skin");
if (type == SkinType.SKIN) {
previewer.getLocal().ifPresent(local -> IPony.getManager().removePony(local.getTextures().get(SkinType.SKIN).getId()));
}
}
@Override
public void onSetRemoteSkin(SkinType type, Identifier location, MinecraftProfileTexture profileTexture) {
super.onSetRemoteSkin(type, location, profileTexture);
MineLittlePony.logger.debug("Invalidating old remote skin, checking updated remote skin");
if (type == SkinType.SKIN) {
IPony.getManager().removePony(location);
}
}
}

View file

@ -1,17 +1,17 @@
package com.minelittlepony.client.compat.hdskins; package com.minelittlepony.client.hdskins;
import com.minelittlepony.api.config.PonyConfig; import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.config.PonyLevel; import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.api.pony.*;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.client.SkinsProxy;
import com.minelittlepony.common.client.gui.ScrollContainer; import com.minelittlepony.common.client.gui.ScrollContainer;
import com.minelittlepony.common.client.gui.element.Button; import com.minelittlepony.common.client.gui.element.Button;
import com.minelittlepony.common.event.ClientReadyCallback; import com.minelittlepony.common.event.ClientReadyCallback;
import com.minelittlepony.hdskins.client.*; import com.minelittlepony.hdskins.client.*;
import com.minelittlepony.hdskins.client.dummy.DummyPlayer;
import com.minelittlepony.hdskins.client.dummy.PlayerSkins.PlayerSkin;
import com.minelittlepony.hdskins.client.gui.GuiSkins; 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.minelittlepony.hdskins.profile.SkinType;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
@ -28,7 +28,8 @@ import net.minecraft.entity.Entity;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import com.minelittlepony.client.*; import com.minelittlepony.client.pony.PonyManager;
import com.minelittlepony.client.render.entity.PlayerSeaponyRenderer;
/** /**
* All the interactions with HD Skins. * All the interactions with HD Skins.
@ -36,18 +37,15 @@ import com.minelittlepony.client.*;
public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer { public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer {
static SkinType seaponySkinType; static SkinType seaponySkinType;
static SkinType nirikSkinType;
static final Map<SkinType, Wearable> wearableTypes = new HashMap<>(); static final Map<SkinType, Wearable> wearableTypes = new HashMap<>();
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
SkinsProxy.instance = this; SkinsProxy.instance = this;
PonySettingsScreen.buttonFactory = this::renderOption;
seaponySkinType = SkinType.register(DefaultPonySkinHelper.SEAPONY_SKIN_TYPE_ID, Items.COD_BUCKET.getDefaultStack()); seaponySkinType = SkinType.register(PlayerSeaponyRenderer.SKIN_TYPE_ID, Items.COD_BUCKET.getDefaultStack());
nirikSkinType = SkinType.register(DefaultPonySkinHelper.NIRIK_SKIN_TYPE_ID, Items.LAVA_BUCKET.getDefaultStack()); Wearable.VALUES.forEach(wearable -> {
Wearable.REGISTRY.values().forEach(wearable -> {
if (wearable != Wearable.NONE) { if (wearable != Wearable.NONE) {
wearableTypes.put(SkinType.register(wearable.getId(), Items.BUNDLE.getDefaultStack()), wearable); wearableTypes.put(SkinType.register(wearable.getId(), Items.BUNDLE.getDefaultStack()), wearable);
} }
@ -55,34 +53,16 @@ public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer {
ClientReadyCallback.EVENT.register(client -> { ClientReadyCallback.EVENT.register(client -> {
// Clear ponies when skins are cleared // Clear ponies when skins are cleared
SkinCacheClearCallback.EVENT.register(MineLittlePony.getInstance().getManager()::clearCache); PonyManager ponyManager = (PonyManager) MineLittlePony.getInstance().getManager();
SkinCacheClearCallback.EVENT.register(ponyManager::clearCache);
// Ponify the skins GUI. // Ponify the skins GUI.
GuiSkins.setSkinsGui(GuiSkinsMineLP::new); GuiSkins.setSkinsGui(GuiSkinsMineLP::new);
}); });
HDSkins.getInstance().getSkinPrioritySorter().addSelector((skinType, playerSkins) -> {
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()
&& 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();
});
} }
static Optional<Pony> getPony(PlayerSkinLayers.Layer layer) { @Override
return layer public void renderOption(Screen screen, @Nullable Screen parent, int row, int RIGHT, ScrollContainer content) {
.getSkin(SkinType.SKIN)
.map(Pony.getManager()::getPony);
}
private void renderOption(Screen screen, @Nullable Screen parent, int row, int RIGHT, ScrollContainer content) {
content.addButton(new Button(RIGHT, row += 20, 150, 20)) content.addButton(new Button(RIGHT, row += 20, 150, 20))
.onClick(button -> MinecraftClient.getInstance().setScreen( .onClick(button -> MinecraftClient.getInstance().setScreen(
parent instanceof GuiSkins ? parent : GuiSkins.create(screen, HDSkins.getInstance().getSkinServerList()) parent instanceof GuiSkins ? parent : GuiSkins.create(screen, HDSkins.getInstance().getSkinServerList())
@ -103,11 +83,10 @@ public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer {
} }
if (entity instanceof AbstractClientPlayerEntity player) { if (entity instanceof AbstractClientPlayerEntity player) {
return PlayerSkins.of(player) PlayerSkins skins = PlayerSkins.of(player);
.map(PlayerSkins::layers) if (skins != null) {
.map(PlayerSkinLayers::combined) return skins.getProvidedSkinTypes();
.map(PlayerSkinLayers.Layer::getProvidedSkinTypes) }
.orElseGet(Set::of);
} }
return Set.of(); return Set.of();
@ -123,10 +102,10 @@ public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer {
PlayerSkin main = dummy.getTextures().get(SkinType.SKIN); PlayerSkin main = dummy.getTextures().get(SkinType.SKIN);
Wearable wearable = Wearable.REGISTRY.getOrDefault(type.getId(), Wearable.NONE); Wearable wearable = Wearable.REGISTRY.getOrDefault(type.getId(), Wearable.NONE);
PonyData metadata = Pony.getManager().getPony(main.getId()).metadata(); IPonyData metadata = IPony.getManager().getPony(main.getId()).metadata();
if (wearable != Wearable.NONE && metadata.gear().matches(wearable)) { if (wearable != Wearable.NONE && metadata.isWearing(wearable)) {
if (wearable.isSaddlebags() && metadata.race().supportsLegacySaddlebags()) { if (wearable.isSaddlebags() && metadata.getRace().supportsLegacySaddlebags()) {
return Optional.of(main.getId()); return Optional.of(main.getId());
} }
@ -134,17 +113,18 @@ public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer {
} }
} }
return Optional.of(player).flatMap(PlayerSkins::of) return Optional.of(player).map(PlayerSkins::of).map(skins -> skins.getSkin(type));
.map(PlayerSkins::layers)
.map(PlayerSkinLayers::combined).flatMap(skins -> skins.getSkin(type));
} }
@Override @Override
public Identifier getSkinTexture(GameProfile profile) { public Identifier getSkinTexture(GameProfile profile) {
return HDSkins.getInstance().getProfileRepository()
.load(profile) Identifier skin = HDSkins.getInstance().getProfileRepository().getTextures(profile).get(SkinType.SKIN);
.getNow(ProvidedSkins.EMPTY)
.getSkin(SkinType.SKIN) if (skin != null) {
.orElseGet(() -> super.getSkinTexture(profile)); return skin;
}
return super.getSkinTexture(profile);
} }
} }

View file

@ -0,0 +1,113 @@
package com.minelittlepony.client.hdskins;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.pony.*;
import com.minelittlepony.api.pony.meta.TriggerPixel;
import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.render.entity.SeaponyRenderer;
import com.minelittlepony.common.client.gui.dimension.Bounds;
import com.minelittlepony.hdskins.client.dummy.*;
import com.minelittlepony.hdskins.client.resources.DefaultSkinGenerator;
import com.minelittlepony.hdskins.client.resources.TextureLoader;
import com.minelittlepony.hdskins.profile.SkinType;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;
class PonyPreview extends PlayerPreview {
@Override
protected DummyPlayer createEntity(ClientWorld world, PlayerSkins<?> textures) {
return new DummyPony(world, textures);
}
@Override
public Identifier getDefaultSkin(SkinType type, boolean slim) {
if (type == MineLPHDSkins.seaponySkinType) {
return DefaultSkinGenerator.generateGreyScale(SeaponyRenderer.TEXTURE, SeaponyRenderer.TEXTURE, getExclusion());
}
Wearable wearable = MineLPHDSkins.wearableTypes.getOrDefault(type, Wearable.NONE);
if (wearable != Wearable.NONE) {
return DefaultSkinGenerator.generateGreyScale(wearable.getDefaultTexture(), wearable.getDefaultTexture(), getExclusion());
}
return super.getDefaultSkin(type, slim);
}
@Override
protected TextureLoader.Exclusion getExclusion() {
return TriggerPixel::isTriggerPixelCoord;
}
@Override
public void renderWorldAndPlayer(Optional<DummyPlayer> thePlayer,
Bounds frame,
int horizon, int mouseX, int mouseY, int ticks, float partialTick, float scale,
MatrixStack matrices, @Nullable Consumer<DummyPlayer> postAction) {
super.renderWorldAndPlayer(thePlayer, frame, horizon, mouseX, mouseY, ticks, partialTick, scale, matrices, postAction);
thePlayer.ifPresent(p -> {
IPonyData data = IPony.getManager().getPony(p).metadata();
int[] index = new int[1];
data.getTriggerPixels().forEach((key, value) -> {
drawLegendBlock(matrices, index[0]++, frame.left, frame.top, mouseX, mouseY, key, value);
});
});
}
private void drawLegendBlock(MatrixStack matrices, int index, int x, int y, int mouseX, int mouseY, String key, TriggerPixelType<?> value) {
int size = 10;
int yPos = y + index * size + 20;
fill(matrices,
x, yPos,
x + size, yPos + size,
0xFF003333
);
fill(matrices,
x + 1, yPos + 1,
x - 1 + size, yPos - 1 + size,
value.getColorCode() | 0xFF000000
);
char symbol = value.name().charAt(0);
if (symbol == '[') {
symbol = key.charAt(0);
}
minecraft.textRenderer.drawWithShadow(matrices,
Text.of(String.valueOf(symbol).toUpperCase()),
x + 2,
yPos + 1,
0xFFFFFFFF
);
if (mouseX > x && mouseX < (x + size) && mouseY > yPos && mouseY < (yPos + size)) {
List<Text> lines = value.getOptions().stream().map(option -> {
boolean selected = value.matches(option);
return Text.literal((selected ? "* " : " ") + option.name()).styled(s -> {
int color = option.getChannelAdjustedColorCode();
return (color == 0 ? s : s.withColor(color)).withItalic(selected);
});
}).collect(Collectors.toList());
lines.add(0, Text.of(key.toUpperCase() + ": " + value.getHexValue()));
if (lines.size() == 1) {
lines.add(Text.literal(value.name()).styled(s -> {
int color = value.getChannelAdjustedColorCode();
return color == 0 ? s : s.withColor(value.getColorCode());
}));
}
minecraft.currentScreen.renderTooltip(matrices, lines, mouseX, mouseY);
}
}
}

View file

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

View file

@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.api.pony.Pony; import com.minelittlepony.api.pony.IPony;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera; import net.minecraft.client.render.Camera;
@ -18,10 +18,10 @@ abstract class MixinCamera {
private void redirectCameraDistance(double initial, CallbackInfoReturnable<Double> info) { private void redirectCameraDistance(double initial, CallbackInfoReturnable<Double> info) {
double value = info.getReturnValueD(); double value = info.getReturnValueD();
Pony pony = Pony.getManager().getPony(MinecraftClient.getInstance().player); IPony pony = IPony.getManager().getPony(MinecraftClient.getInstance().player);
if (!pony.race().isHuman()) { if (!pony.race().isHuman()) {
value *= pony.size().eyeDistanceFactor(); value *= pony.metadata().getSize().getEyeDistanceFactor();
} }
info.setReturnValue(value); info.setReturnValue(value);

View file

@ -1,6 +1,7 @@
package com.minelittlepony.client.mixin; package com.minelittlepony.client.mixin;
import com.minelittlepony.api.pony.Pony; import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.client.pony.Pony;
import com.minelittlepony.client.render.EquineRenderManager; import com.minelittlepony.client.render.EquineRenderManager;
import net.minecraft.client.network.AbstractClientPlayerEntity; import net.minecraft.client.network.AbstractClientPlayerEntity;
@ -9,6 +10,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityDimensions; import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityPose; import net.minecraft.entity.EntityPose;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@ -19,7 +21,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
abstract class MixinClientPlayerEntity extends AbstractClientPlayerEntity implements EquineRenderManager.RegistrationHandler { abstract class MixinClientPlayerEntity extends AbstractClientPlayerEntity implements EquineRenderManager.RegistrationHandler {
public MixinClientPlayerEntity() { super(null, null); } public MixinClientPlayerEntity() { super(null, null); }
private final EquineRenderManager.SyncedPony syncedPony = new EquineRenderManager.SyncedPony(); @Nullable
private IPony pony;
@Inject(method = "startRiding(Lnet/minecraft/entity/Entity;Z)Z", at = @At("RETURN")) @Inject(method = "startRiding(Lnet/minecraft/entity/Entity;Z)Z", at = @At("RETURN"))
private void onStartRiding(Entity entity, boolean bl, CallbackInfoReturnable<Boolean> info) { private void onStartRiding(Entity entity, boolean bl, CallbackInfoReturnable<Boolean> info) {
@ -32,24 +35,30 @@ abstract class MixinClientPlayerEntity extends AbstractClientPlayerEntity implem
} }
@Override @Override
public EquineRenderManager.SyncedPony getSyncedPony() { public boolean shouldUpdateRegistration(IPony pony) {
return syncedPony; if (this.pony != pony && (this.pony == null || this.pony.metadata().compareTo(pony.metadata()) != 0)) {
this.pony = Pony.snapshot(pony);
return true;
}
return false;
} }
@Override @Override
public float getActiveEyeHeight(EntityPose pose, EntityDimensions dimensions) { public float getActiveEyeHeight(EntityPose pose, EntityDimensions dimensions) {
float value = super.getActiveEyeHeight(pose, dimensions); float value = super.getActiveEyeHeight(pose, dimensions);
Pony pony = Pony.getManager().getPony(this); IPony pony = IPony.getManager().getPony(this);
if (!pony.race().isHuman()) { if (!pony.race().isHuman()) {
float factor = pony.size().eyeHeightFactor(); float factor = pony.metadata().getSize().getEyeHeightFactor();
if (factor != 1) { if (factor != 1) {
value *= factor; value *= factor;
if (hasVehicle()) { if (hasVehicle()) {
value += getVehicle().getHeight(); value += getVehicle().getEyeHeight(getVehicle().getPose());
value -= getVehicle().getMountedHeightOffset();
} }
return Math.max(value, 0.1F); return Math.max(value, 0.1F);
} }
} }

View file

@ -1,13 +1,12 @@
package com.minelittlepony.client.mixin; package com.minelittlepony.client.mixin;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.config.PonyLevel; import com.minelittlepony.api.config.PonyLevel;
import com.minelittlepony.api.pony.DefaultPonySkinHelper; import com.minelittlepony.api.pony.DefaultPonySkinHelper;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.client.MineLittlePony;
import net.minecraft.client.util.DefaultSkinHelper; import net.minecraft.client.util.DefaultSkinHelper;
import net.minecraft.client.util.SkinTextures;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@ -21,17 +20,29 @@ abstract class MixinDefaultSkinHelper {
at = @At("RETURN"), at = @At("RETURN"),
cancellable = true) cancellable = true)
private static void onGetTexture(CallbackInfoReturnable<Identifier> cir) { private static void onGetTexture(CallbackInfoReturnable<Identifier> cir) {
if (PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES) { if (MineLittlePony.getInstance().getConfig().ponyLevel.get() == PonyLevel.PONIES) {
cir.setReturnValue(DefaultPonySkinHelper.STEVE); cir.setReturnValue(DefaultPonySkinHelper.getPonySkin(cir.getReturnValue()));
} }
} }
@Inject(method = "getSkinTextures(Ljava/util/UUID;)Lnet/minecraft/client/util/SkinTextures;", @Inject(method = "getTexture(Ljava/util/UUID;)Lnet/minecraft/util/Identifier;",
at = @At("RETURN"), at = @At("RETURN"),
cancellable = true) cancellable = true)
private static void onGetTexture(UUID uuid, CallbackInfoReturnable<SkinTextures> cir) { private static void onGetTexture(UUID uuid, CallbackInfoReturnable<Identifier> cir) {
if (PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES) { if (MineLittlePony.getInstance().getConfig().ponyLevel.get() == PonyLevel.PONIES) {
cir.setReturnValue(DefaultPonySkinHelper.getTextures(cir.getReturnValue())); cir.setReturnValue(DefaultPonySkinHelper.getPonySkin(cir.getReturnValue()));
}
}
@Inject(method = "getModel(Ljava/util/UUID;)Ljava/lang/String;",
at = @At("RETURN"),
cancellable = true)
private static void onGetModel(UUID uuid, CallbackInfoReturnable<String> cir) {
if (MineLittlePony.getInstance().getConfig().ponyLevel.get() == PonyLevel.PONIES) {
cir.setReturnValue(IPony.getManager()
.getPony(DefaultSkinHelper.getTexture(uuid), uuid)
.race()
.getModelId("slim".equalsIgnoreCase(cir.getReturnValue())));
} }
} }
} }

View file

@ -0,0 +1,29 @@
package com.minelittlepony.client.mixin;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.client.IPreviewModel;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(EntityRenderDispatcher.class)
abstract class MixinEntityRenderDispatcher {
@Redirect(
method = "getRenderer(Lnet/minecraft/entity/Entity;)Lnet/minecraft/client/render/entity/EntityRenderer;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;getModel()Ljava/lang/String;"))
private String getPlayerModel(AbstractClientPlayerEntity player, Entity entity) {
if (player instanceof IPreviewModel) {
return player.getModel();
}
return IPony.getManager()
.getPony(player)
.race()
.getModelId(player.getModel().contains("slim"));
}
}

View file

@ -4,13 +4,13 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import com.minelittlepony.client.MineLittlePony; import com.minelittlepony.client.render.PonyRenderDispatcher;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import net.minecraft.client.render.item.HeldItemRenderer; import net.minecraft.client.render.item.HeldItemRenderer;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.model.json.ModelTransformationMode; import net.minecraft.client.render.model.json.ModelTransformation;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@ -22,7 +22,7 @@ abstract class MixinHeldItemRenderer {
private static final String LivingEntity = "Lnet/minecraft/entity/LivingEntity;"; private static final String LivingEntity = "Lnet/minecraft/entity/LivingEntity;";
private static final String MatrixStack = "Lnet/minecraft/client/util/math/MatrixStack;"; private static final String MatrixStack = "Lnet/minecraft/client/util/math/MatrixStack;";
private static final String ItemStack = "Lnet/minecraft/item/ItemStack;"; private static final String ItemStack = "Lnet/minecraft/item/ItemStack;";
private static final String Mode = "Lnet/minecraft/client/render/model/json/ModelTransformationMode;"; private static final String Mode = "Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;";
private static final String VertexConsumerProvider = "Lnet/minecraft/client/render/VertexConsumerProvider;"; private static final String VertexConsumerProvider = "Lnet/minecraft/client/render/VertexConsumerProvider;";
private static final String World = "Lnet/minecraft/world/World;"; private static final String World = "Lnet/minecraft/world/World;";
private static final String ItemRenderer = "Lnet/minecraft/client/render/item/ItemRenderer;"; private static final String ItemRenderer = "Lnet/minecraft/client/render/item/ItemRenderer;";
@ -36,12 +36,12 @@ abstract class MixinHeldItemRenderer {
private void redirectRenderItem(ItemRenderer target, private void redirectRenderItem(ItemRenderer target,
@Nullable LivingEntity entity, @Nullable LivingEntity entity,
ItemStack item, ItemStack item,
ModelTransformationMode transform, ModelTransformation.Mode transform,
boolean left, boolean left,
MatrixStack stack, MatrixStack stack,
VertexConsumerProvider renderContext, VertexConsumerProvider renderContext,
@Nullable World world, @Nullable World world,
int lightUv, int overlayUv, int posLong) { int lightUv, int overlayUv, int posLong) {
MineLittlePony.getInstance().getRenderDispatcher().getMagicRenderer().renderItem(target, entity, item, transform, left, stack, renderContext, world, lightUv, posLong); PonyRenderDispatcher.getInstance().getMagicRenderer().renderItemInFirstPerson(target, entity, item, transform, left, stack, renderContext, world, lightUv, posLong);
} }
} }

View file

@ -1,6 +1,6 @@
package com.minelittlepony.client.mixin; package com.minelittlepony.client.mixin;
import net.minecraft.network.packet.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.listener.ServerPlayPacketListener; import net.minecraft.network.listener.ServerPlayPacketListener;
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;

View file

@ -5,9 +5,8 @@ import com.minelittlepony.client.HorseCam;
import java.util.Set; import java.util.Set;
import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
import net.minecraft.network.packet.s2c.play.PositionFlag; import net.minecraft.network.Packet;
import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@ -19,12 +18,12 @@ abstract class MixinPlayerPositionLookS2CPacket implements Packet<ClientPlayPack
@Shadow @Mutable @Shadow @Mutable
private @Final float pitch; private @Final float pitch;
@Shadow @Shadow
private @Final Set<PositionFlag> flags; private @Final Set<PlayerPositionLookS2CPacket.Flag> flags;
@Inject(method = "apply(Lnet/minecraft/network/listener/ClientPlayPacketListener;)V", @Inject(method = "apply(Lnet/minecraft/network/listener/ClientPlayPacketListener;)V",
at = @At("HEAD")) at = @At("HEAD"))
private void onApply(ClientPlayPacketListener clientPlayPacketListener, CallbackInfo info) { private void onApply(ClientPlayPacketListener clientPlayPacketListener, CallbackInfo info) {
if (!flags.contains(PositionFlag.Y_ROT)) { if (!flags.contains(PlayerPositionLookS2CPacket.Flag.Y_ROT)) {
pitch = HorseCam.transformIncomingServerCameraAngle(pitch); pitch = HorseCam.transformIncomingServerCameraAngle(pitch);
} }
} }

View file

@ -1,8 +1,8 @@
package com.minelittlepony.client.model; package com.minelittlepony.client.model;
import com.minelittlepony.api.model.*; import com.minelittlepony.api.model.*;
import com.minelittlepony.api.events.PonyModelPrepareCallback; import com.minelittlepony.api.model.fabric.PonyModelPrepareCallback;
import com.minelittlepony.api.pony.meta.SizePreset; import com.minelittlepony.api.pony.meta.Sizes;
import com.minelittlepony.client.transform.PonyTransformation; import com.minelittlepony.client.transform.PonyTransformation;
import com.minelittlepony.client.util.render.RenderList; import com.minelittlepony.client.util.render.RenderList;
import com.minelittlepony.util.MathUtil; import com.minelittlepony.util.MathUtil;
@ -50,7 +50,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
protected final RenderList mainRenderList; protected final RenderList mainRenderList;
private final List<SubModel> parts = new ArrayList<>(); private final List<IPart> parts = new ArrayList<>();
public AbstractPonyModel(ModelPart tree) { public AbstractPonyModel(ModelPart tree) {
super(tree); super(tree);
@ -66,18 +66,18 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
.add(withStage(BodyPart.HEAD, helmetRenderList = RenderList.of(hat))); .add(withStage(BodyPart.HEAD, helmetRenderList = RenderList.of(hat)));
} }
protected <P extends SubModel> P addPart(P part) { protected <P extends IPart> P addPart(P part) {
parts.add(part); parts.add(part);
return part; return part;
} }
protected RenderList forPart(Supplier<SubModel> part) { protected RenderList forPart(Supplier<IPart> part) {
return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> { return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
part.get().renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes); part.get().renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes);
}; };
} }
protected RenderList forPart(SubModel part) { protected RenderList forPart(IPart part) {
return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> { return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
part.renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes); part.renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes);
}; };
@ -102,7 +102,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
*/ */
@Override @Override
public final void setAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) { public final void setAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) {
attributes.checkRainboom(entity, this, animationProgress); attributes.checkRainboom(entity, canFly(), animationProgress);
PonyModelPrepareCallback.EVENT.invoker().onPonyModelPrepared(entity, this, ModelAttributes.Mode.OTHER); PonyModelPrepareCallback.EVENT.invoker().onPonyModelPrepared(entity, this, ModelAttributes.Mode.OTHER);
super.setAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch); super.setAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch);
@ -119,10 +119,11 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
} }
protected void setModelAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) { protected void setModelAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) {
float pitch = attributes.motionPitch * MathHelper.RADIANS_PER_DEGREE;
float pitch = (float)Math.toRadians(attributes.motionPitch);
head.setAngles( head.setAngles(
MathHelper.clamp(attributes.isSleeping ? 0.1f : headPitch / 57.29578F, -1.25f - pitch, 0.5f - pitch), 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, attributes.isSleeping ? (Math.abs(entity.getUuid().getMostSignificantBits()) % 2.8F) - 1.9F : headYaw / 57.29578F,
0 0
); );
@ -148,7 +149,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
} else { } else {
adjustBody(0, ORIGIN); adjustBody(0, ORIGIN);
if (!attributes.isLyingDown) { if (!attributes.isSleeping) {
animateBreathing(animationProgress); animateBreathing(animationProgress);
} }
@ -158,7 +159,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
} }
} }
if (attributes.isLyingDown) { if (attributes.isSleeping) {
ponySleep(); ponySleep();
} }
@ -196,6 +197,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
leftLeg.pitch = MathUtil.Angles._90_DEG; leftLeg.pitch = MathUtil.Angles._90_DEG;
HEAD_SLEEPING.set(head); HEAD_SLEEPING.set(head);
head.pivotZ = sneaking ? -1 : 1;
FONT_LEGS_SLEEPING.add(rightArm); FONT_LEGS_SLEEPING.add(rightArm);
FONT_LEGS_SLEEPING.add(leftArm); FONT_LEGS_SLEEPING.add(leftArm);
@ -339,7 +341,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
} }
protected float getLegOutset() { protected float getLegOutset() {
if (attributes.isLyingDown) { if (attributes.isSleeping) {
return 3.6f; return 3.6f;
} }
@ -366,7 +368,6 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
case NECK: return neck; case NECK: return neck;
case TAIL: case TAIL:
case LEGS: case LEGS:
case BACK:
case BODY: return body; case BODY: return body;
} }
} }
@ -387,14 +388,13 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
if (attributes.shouldLiftArm(pose, complement, sigma)) { if (attributes.shouldLiftArm(pose, complement, sigma)) {
float swag = 1; float swag = 1;
if (!getAttributes().isFlying && both) { if (!isFlying() && both) {
swag -= (float)Math.pow(limbSpeed, 2); swag -= (float)Math.pow(limbSpeed, 2);
} }
float mult = 1 - swag/2; float mult = 1 - swag/2;
arm.pitch = arm.pitch * mult - (MathHelper.PI / 10) * swag; arm.pitch = arm.pitch * mult - (MathHelper.PI / 10) * swag;
arm.roll = -sigma * (MathHelper.PI / 15); arm.roll = -sigma * (MathHelper.PI / 15);
arm.roll += 0.3F * -limbSpeed * sigma;
if (attributes.isCrouching) { if (attributes.isCrouching) {
arm.pivotX -= sigma * 2; arm.pivotX -= sigma * 2;
@ -408,7 +408,6 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
case BLOCK: case BLOCK:
arm.pitch = (arm.pitch / 2 - 0.9424779F) - 0.3F; arm.pitch = (arm.pitch / 2 - 0.9424779F) - 0.3F;
arm.yaw = sigma * MathHelper.PI / 9; arm.yaw = sigma * MathHelper.PI / 9;
arm.roll += 0.3F * -limbSpeed * sigma;
if (complement == pose) { if (complement == pose) {
arm.yaw -= sigma * MathHelper.PI / 18; arm.yaw -= sigma * MathHelper.PI / 18;
} }
@ -432,12 +431,9 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
arm.pitch = -0.8F; arm.pitch = -0.8F;
arm.yaw = head.yaw + 0.06F; arm.yaw = head.yaw + 0.06F;
arm.roll += 0.3F * -limbSpeed * sigma;
break; break;
case THROW_SPEAR: case THROW_SPEAR:
arm.pitch = MathUtil.Angles._90_DEG * 2; arm.pitch = MathUtil.Angles._90_DEG * 2;
arm.roll += (0.3F * -limbSpeed + 0.6F) * sigma;
arm.pivotY ++;
break; break;
case SPYGLASS: case SPYGLASS:
float addedPitch = sneaking ? -0.2617994F : 0; float addedPitch = sneaking ? -0.2617994F : 0;
@ -450,24 +446,13 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
arm.pivotX -= 6 * sigma; arm.pivotX -= 6 * sigma;
arm.pivotZ -= 2; arm.pivotZ -= 2;
} }
if (getSize() == SizePreset.TALL) { if (getSize() == Sizes.TALL) {
arm.pivotY += 1; arm.pivotY += 1;
} }
if (getSize() == SizePreset.FOAL) { if (getSize() == Sizes.FOAL) {
arm.pivotY -= 2; arm.pivotY -= 2;
} }
break;
case TOOT_HORN:
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 * -limbSpeed * sigma;
break;
case BRUSH:
arm.pitch = arm.pitch * 0.5f - 0.62831855f;
arm.yaw = 0;
arm.roll += 0.3F * -limbSpeed * sigma;
break; break;
default: default:
break; break;
@ -490,7 +475,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
* @param entity The entity we are being called for. * @param entity The entity we are being called for.
*/ */
protected void swingItem(T entity) { protected void swingItem(T entity) {
if (getSwingAmount() > 0 && !attributes.isLyingDown) { if (getSwingAmount() > 0 && !attributes.isSleeping) {
Arm mainSide = getPreferredArm(entity); Arm mainSide = getPreferredArm(entity);
swingArm(getArm(mainSide)); swingArm(getArm(mainSide));
@ -558,7 +543,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
@Override @Override
public float getRiderYOffset() { public float getRiderYOffset() {
switch ((SizePreset)getSize()) { switch ((Sizes)getSize()) {
case NORMAL: return 0.4F; case NORMAL: return 0.4F;
case FOAL: case FOAL:
case TALL: case TALL:
@ -604,11 +589,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
matrices.translate(left / 10, -0.2F, -0.5F); matrices.translate(left / 10, -0.2F, -0.5F);
} }
matrices.translate(-left * 0.1F, 0.45F, 0); matrices.translate(left * 0.1F, 0.45F, 0);
if (getAttributes().heldStack.getUseAction() == UseAction.BLOCK && getAttributes().itemUseTime == 0) {
matrices.translate(left * 0.02F, -0.25F, 0);
}
} }
@Override @Override
@ -623,25 +604,14 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180)); stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180));
} }
boolean crouching = attributes.isCrouching;
if (attributes.isLyingDown && !attributes.isSleeping) {
stack.translate(0, 1.35F, 0);
attributes.isCrouching = sneaking;
}
if (attributes.isHorsey) { if (attributes.isHorsey) {
if (part == BodyPart.BODY) { if (part == BodyPart.BODY) {
stack.scale(1.5F, 1, 1.5F); stack.scale(1.5F, 1, 1.5F);
} }
neck.visible = head.visible; neck.visible = head.visible;
} else {
neck.hidden = !head.visible;
} }
PonyTransformation.forSize(getSize()).transform(this, part, stack); PonyTransformation.forSize(getSize()).transform(this, part, stack);
attributes.isCrouching = crouching;
} }
} }

View file

@ -8,12 +8,13 @@ import net.minecraft.util.Hand;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.events.PonyModelPrepareCallback; import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.model.*; import com.minelittlepony.api.model.IPart;
import com.minelittlepony.api.pony.Pony; import com.minelittlepony.api.model.fabric.PonyModelPrepareCallback;
import com.minelittlepony.api.pony.PonyData; import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.api.pony.meta.Size; import com.minelittlepony.api.pony.meta.Size;
import com.minelittlepony.api.pony.meta.SizePreset; import com.minelittlepony.api.pony.meta.Sizes;
import com.minelittlepony.mson.api.model.biped.MsonPlayer; import com.minelittlepony.mson.api.model.biped.MsonPlayer;
/** /**
@ -23,7 +24,7 @@ import com.minelittlepony.mson.api.model.biped.MsonPlayer;
* *
* Modders can extend this class to make their own pony models if they wish. * Modders can extend this class to make their own pony models if they wish.
*/ */
public abstract class ClientPonyModel<T extends LivingEntity> extends MsonPlayer<T> implements PonyModel<T> { public abstract class ClientPonyModel<T extends LivingEntity> extends MsonPlayer<T> implements IPonyModel<T>, ModelWithHat, ModelWithArms {
/** /**
* The model attributes. * The model attributes.
@ -47,11 +48,12 @@ public abstract class ClientPonyModel<T extends LivingEntity> extends MsonPlayer
} }
@Override @Override
public void updateLivingState(T entity, Pony pony, ModelAttributes.Mode mode) { public void updateLivingState(T entity, IPony pony, ModelAttributes.Mode mode) {
IPart.Compat.attributes = attributes;
child = entity.isBaby(); child = entity.isBaby();
attributes.updateLivingState(entity, pony, mode); attributes.updateLivingState(entity, pony, mode);
PonyModelPrepareCallback.EVENT.invoker().onPonyModelPrepared(entity, this, mode); PonyModelPrepareCallback.EVENT.invoker().onPonyModelPrepared(entity, this, mode);
sneaking = attributes.isCrouching && !attributes.isLyingDown; sneaking = attributes.isCrouching;
riding = attributes.isSitting; riding = attributes.isSitting;
} }
@ -60,6 +62,35 @@ public abstract class ClientPonyModel<T extends LivingEntity> extends MsonPlayer
copyStateTo(other); copyStateTo(other);
} }
@Override
public final ModelAttributes getAttributes() {
return attributes;
}
@Override
public Size getSize() {
return child ? Sizes.FOAL : getMetadata().getSize();
}
@Override
public void setMetadata(IPonyData meta) {
attributes.metadata = meta;
}
@Override
public float getSwingAmount() {
return handSwingProgress;
}
@Override
public ModelPart getArm(Arm side) {
return super.getArm(side);
}
public ArmPose getArmPoseForSide(Arm side) {
return side == Arm.RIGHT ? rightArmPose : leftArmPose;
}
/** /**
* Copies this model's attributes into the passed model. * Copies this model's attributes into the passed model.
*/ */
@ -72,41 +103,6 @@ public abstract class ClientPonyModel<T extends LivingEntity> extends MsonPlayer
} }
} }
@Override
public final ModelAttributes getAttributes() {
return attributes;
}
@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
public ModelPart getForeLeg(Arm side) {
return getArm(side);
}
@Override
public ModelPart getHindLeg(Arm side) {
return side == Arm.LEFT ? leftLeg : rightLeg;
}
@Override
public ArmPose getArmPoseForSide(Arm side) {
return side == Arm.RIGHT ? rightArmPose : leftArmPose;
}
@Override @Override
public void setHatVisible(boolean visible) { public void setHatVisible(boolean visible) {

View file

@ -1,4 +1,4 @@
package com.minelittlepony.api.model; package com.minelittlepony.client.model;
import net.minecraft.client.model.ModelPart; import net.minecraft.client.model.ModelPart;
import net.minecraft.util.Arm; import net.minecraft.util.Arm;
@ -6,7 +6,10 @@ import net.minecraft.util.math.MathHelper;
import com.minelittlepony.mson.util.PartUtil; import com.minelittlepony.mson.util.PartUtil;
public final class MobPosingHelper { /**
* Common interface for all undead enemies.
*/
public interface IMobModel {
/** /**
* Rotates the provided arm to the correct orientation for holding an item. * Rotates the provided arm to the correct orientation for holding an item.
* *
@ -15,7 +18,7 @@ public final class MobPosingHelper {
* @param swingProgress How far we are through the current swing * @param swingProgress How far we are through the current swing
* @param ticks Render partial ticks * @param ticks Render partial ticks
*/ */
public static void rotateArmHolding(ModelPart arm, float direction, float swingProgress, float ticks) { static void rotateArmHolding(ModelPart arm, float direction, float swingProgress, float ticks) {
float swing = MathHelper.sin(swingProgress * MathHelper.PI); float swing = MathHelper.sin(swingProgress * MathHelper.PI);
float roll = MathHelper.sin((1 - (1 - swingProgress) * (1 - swingProgress)) * MathHelper.PI); float roll = MathHelper.sin((1 - (1 - swingProgress) * (1 - swingProgress)) * MathHelper.PI);
@ -30,26 +33,20 @@ public final class MobPosingHelper {
arm.roll = cos; arm.roll = cos;
} }
public static void rotateUndeadArms(PonyModel<?> model, float move, float ticks) { static void rotateUndeadArms(ClientPonyModel<?> model, float move, float ticks) {
ModelPart leftArm = model.getForeLeg(Arm.LEFT); ModelPart leftArm = model.getArm(Arm.LEFT);
ModelPart rightArm = model.getForeLeg(Arm.RIGHT); ModelPart rightArm = model.getArm(Arm.RIGHT);
if (islookAngleRight(move)) { if (islookAngleRight(move)) {
rotateArmHolding(rightArm, 1, model.getSwingAmount(), ticks); IMobModel.rotateArmHolding(rightArm, 1, model.getSwingAmount(), ticks);
if (model.getAttributes().isSitting) {
rightArm.pitch += 0.6F;
}
PartUtil.shift(rightArm, 0.5F, 1.5F, 3); PartUtil.shift(rightArm, 0.5F, 1.5F, 3);
} else { } else {
rotateArmHolding(leftArm, -1, model.getSwingAmount(), ticks); IMobModel.rotateArmHolding(leftArm, -1, model.getSwingAmount(), ticks);
if (model.getAttributes().isSitting) {
leftArm.pitch += 0.6F;
}
PartUtil.shift(leftArm, -0.5F, 1.5F, 3); PartUtil.shift(leftArm, -0.5F, 1.5F, 3);
} }
} }
public static boolean islookAngleRight(float move) { static boolean islookAngleRight(float move) {
return MathHelper.sin(move / 20) < 0; return MathHelper.sin(move / 20) < 0;
} }
} }

View file

@ -1,20 +1,23 @@
package com.minelittlepony.api.model; package com.minelittlepony.client.model;
import net.minecraft.client.model.ModelPart; import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.BipedEntityModel; import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.ModelWithArms; 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.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm; import net.minecraft.util.Arm;
import com.minelittlepony.api.pony.Pony; import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.pony.PonyData; import com.minelittlepony.api.model.IUnicorn;
import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.api.pony.meta.Size; import com.minelittlepony.api.pony.meta.Size;
import com.minelittlepony.mson.api.ModelView; import com.minelittlepony.mson.api.ModelView;
import com.minelittlepony.mson.api.model.BoxBuilder.RenderLayerSetter; import com.minelittlepony.mson.api.model.BoxBuilder.RenderLayerSetter;
public interface PonyModelMixin<T extends LivingEntity, M extends PonyModel<T>> extends PonyModel<T> { public interface IPonyMixinModel<T extends LivingEntity, M extends IPonyModel<T>> extends IPonyModel<T>, ModelWithArms {
M mixin(); M mixin();
@Override @Override
@ -26,7 +29,7 @@ public interface PonyModelMixin<T extends LivingEntity, M extends PonyModel<T>>
} }
@Override @Override
default void updateLivingState(T entity, Pony pony, ModelAttributes.Mode mode) { default void updateLivingState(T entity, IPony pony, ModelAttributes.Mode mode) {
mixin().updateLivingState(entity, pony, mode); mixin().updateLivingState(entity, pony, mode);
} }
@ -51,7 +54,7 @@ public interface PonyModelMixin<T extends LivingEntity, M extends PonyModel<T>>
} }
@Override @Override
default void setMetadata(PonyData meta) { default void setMetadata(IPonyData meta) {
mixin().setMetadata(meta); mixin().setMetadata(meta);
} }
@ -70,22 +73,6 @@ public interface PonyModelMixin<T extends LivingEntity, M extends PonyModel<T>>
return mixin().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 @Override
default void setArmAngle(Arm arm, MatrixStack stack) { default void setArmAngle(Arm arm, MatrixStack stack) {
if (mixin() instanceof ModelWithArms) { if (mixin() instanceof ModelWithArms) {
@ -103,12 +90,7 @@ public interface PonyModelMixin<T extends LivingEntity, M extends PonyModel<T>>
return mixin().getBodyPart(part); return mixin().getBodyPart(part);
} }
@Override interface Caster<T extends LivingEntity, M extends IPonyModel<T> & IUnicorn, ArmModel> extends IPonyMixinModel<T, M>, IUnicorn {
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 @Override
default boolean isCasting() { default boolean isCasting() {
return mixin().isCasting(); return mixin().isCasting();

View file

@ -0,0 +1,22 @@
package com.minelittlepony.client.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.entity.LivingEntity;
import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.model.ICapitated;
import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.mson.api.MsonModel;
public interface IPonyModel<T extends LivingEntity> extends IModel, ICapitated<ModelPart>, ModelWithArms, MsonModel {
void copyAttributes(BipedEntityModel<T> other);
void updateLivingState(T entity, IPony pony, ModelAttributes.Mode mode);
ModelPart getBodyPart(BodyPart part);
}

View file

@ -6,7 +6,7 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.Util; import net.minecraft.util.Util;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.minelittlepony.api.config.PonyConfig; import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.mson.api.*; import com.minelittlepony.mson.api.*;
import com.minelittlepony.mson.api.MsonModel.Factory; import com.minelittlepony.mson.api.MsonModel.Factory;
import com.minelittlepony.mson.api.model.traversal.PartSkeleton; import com.minelittlepony.mson.api.model.traversal.PartSkeleton;
@ -79,7 +79,7 @@ final class ModelKeyImpl<M extends Model> implements ModelKey<M>, LocalBlock {
} }
private ModelContext getModelContext(FileContent<?> content) { private ModelContext getModelContext(FileContent<?> content) {
if (PonyConfig.getInstance().horsieMode.get()) { if (MineLittlePony.getInstance().getConfig().horsieMode.get()) {
return content.createContext(null, null, content.getLocals().extendWith(getId(), Optional.of(this), Optional.empty()).bake()); return content.createContext(null, null, content.getLocals().extendWith(getId(), Optional.of(this), Optional.empty()).bake());
} }
return content.createContext(null, null, content.getLocals().bake()); return content.createContext(null, null, content.getLocals().bake());

View file

@ -8,15 +8,16 @@ import net.minecraft.entity.mob.VexEntity;
import net.minecraft.entity.passive.*; import net.minecraft.entity.passive.*;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import com.minelittlepony.api.model.BodyPart; import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.model.PonyModel; import com.minelittlepony.api.model.gear.IGear;
import com.minelittlepony.api.model.gear.*;
import com.minelittlepony.api.pony.meta.Race; import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.model.armour.PonyArmourModel; import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.client.model.entity.*; import com.minelittlepony.client.model.entity.*;
import com.minelittlepony.client.model.entity.race.*; import com.minelittlepony.client.model.entity.race.*;
import com.minelittlepony.client.model.gear.*; import com.minelittlepony.client.model.gear.*;
import com.minelittlepony.client.render.entity.PlayerPonyRenderer;
import com.minelittlepony.client.render.entity.PlayerSeaponyRenderer;
import com.minelittlepony.mson.api.ModelKey; import com.minelittlepony.mson.api.ModelKey;
import com.minelittlepony.mson.api.Mson; import com.minelittlepony.mson.api.Mson;
import com.minelittlepony.mson.api.MsonModel; import com.minelittlepony.mson.api.MsonModel;
@ -30,7 +31,7 @@ import java.util.stream.Stream;
public final class ModelType { 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<>(); private static final Map<Wearable, GearModelKey<? extends IGear>> GEAR_MODELS = new HashMap<>();
public static final ModelKey<DJPon3EarsModel> DJ_PON_3 = register("dj_pon_three", DJPon3EarsModel::new); public static final ModelKey<DJPon3EarsModel> DJ_PON_3 = register("dj_pon_three", DJPon3EarsModel::new);
@ -56,49 +57,62 @@ public final class ModelType {
public static final ModelKey<PonyArmourModel<?>> INNER_PONY_ARMOR = register("armor/inner_pony_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 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(Wearable.STETSON, BodyPart.HEAD, 0.15F)); public static final GearModelKey<Stetson> STETSON = registerGear("stetson", Wearable.STETSON, Stetson::new);
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_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_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<SaddleBags> SADDLEBAGS_RIGHT = registerGear(SADDLEBAGS_BOTH, Wearable.SADDLE_BAGS_RIGHT, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_RIGHT));
public static final GearModelKey<Crown> CROWN = registerGear("crown", Wearable.CROWN, Crown::new); 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<Muffin> MUFFIN = registerGear("muffin", Wearable.MUFFIN, Muffin::new);
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<WitchHat> WITCH_HAT = registerGear("witch_hat", Wearable.HAT, WitchHat::new);
public static final GearModelKey<DeerAntlers> ANTLERS = registerGear("antlers", Wearable.ANTLERS, DeerAntlers::new); public static final GearModelKey<ChristmasHat> ANTLERS = registerGear("antlers", Wearable.ANTLERS, ChristmasHat::new);
public static final PlayerModelKey<LivingEntity, AlicornModel<?>> ALICORN = registerPlayer("alicorn", Race.ALICORN, AlicornModel::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, 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, 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<?>> 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<?>> 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, PegasusModel<?>> HIPPOGRIFF = registerPlayer("hippogriff", Race.HIPPOGRIFF, PegasusModel::new, PonyArmourModel::new, (ctx, slim, dry) -> {
return new PlayerSeaponyRenderer(ctx, slim, getPlayerModel(Race.SEAPONY), dry);
});
public static final PlayerModelKey<LivingEntity, EarthPonyModel<?>> EARTH_PONY = registerPlayer("earth_pony", Race.EARTH, EarthPonyModel::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, SeaponyModel<?>> SEA_PONY = registerPlayer("sea_pony", Race.SEAPONY, SeaponyModel::new, SeaponyModel.Armour::new, (ctx, slim, wet) -> {
return new PlayerSeaponyRenderer(ctx, slim, wet, getPlayerModel(Race.UNICORN));
});
public static final PlayerModelKey<LivingEntity, PegasusModel<?>> BAT_PONY = registerPlayer("bat_pony", Race.BATPONY, PegasusModel::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<?>> 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, ChangelingModel<?>> CHANGEDLING = registerPlayer("reformed_changeling", Race.CHANGEDLING, ChangelingModel::new);
public static final PlayerModelKey<LivingEntity, EarthPonyModel<?>> ZEBRA = registerPlayer("zebra", Race.ZEBRA, EarthPonyModel::new); public static final PlayerModelKey<LivingEntity, EarthPonyModel<?>> ZEBRA = registerPlayer("zebra", Race.ZEBRA, EarthPonyModel::new);
static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<E, T> registerPlayer(String name, Race race, static <E extends LivingEntity, T extends Model & MsonModel & IModel> PlayerModelKey<E, T> registerPlayer(String name, Race race,
BiFunction<ModelPart, Boolean, T> constructor) { BiFunction<ModelPart, Boolean, T> constructor) {
return registerPlayer(name, race, constructor, PonyArmourModel::new); return registerPlayer(name, race, constructor, PlayerPonyRenderer::new);
} }
@SuppressWarnings("unchecked") static <E extends LivingEntity, T extends Model & MsonModel & IModel> PlayerModelKey<E, 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, BiFunction<ModelPart, Boolean, T> constructor,
MsonModel.Factory<PonyArmourModel<E>> armorFactory) { PlayerModelKey.RendererFactory rendererFactory) {
return (PlayerModelKey<E, T>)PLAYER_MODELS.computeIfAbsent(race, r -> new PlayerModelKey<>(name, constructor, armorFactory)); return registerPlayer(name, race, constructor, PonyArmourModel::new, rendererFactory);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends AbstractGearModel> GearModelKey<T> registerGear(String name, Wearable wearable, MsonModel.Factory<T> constructor) { static <E extends LivingEntity, T extends Model & MsonModel & IModel> PlayerModelKey<E, T> registerPlayer(String name, Race race,
BiFunction<ModelPart, Boolean, T> constructor,
MsonModel.Factory<PonyArmourModel<E>> armorFactory,
PlayerModelKey.RendererFactory rendererFactory) {
return (PlayerModelKey<E, T>)PLAYER_MODELS.computeIfAbsent(race, r -> {
return new PlayerModelKey<>(name, constructor, rendererFactory, armorFactory);
});
}
@SuppressWarnings("unchecked")
static <T extends AbstractGear> GearModelKey<T> registerGear(String name, Wearable wearable, MsonModel.Factory<T> constructor) {
return (GearModelKey<T>)GEAR_MODELS.computeIfAbsent(wearable, w -> { return (GearModelKey<T>)GEAR_MODELS.computeIfAbsent(wearable, w -> {
return new GearModelKey<T>(Mson.getInstance().registerModel(new Identifier("minelittlepony", "gear/" + name), constructor), constructor); return new GearModelKey<T>(Mson.getInstance().registerModel(new Identifier("minelittlepony", "gear/" + name), constructor), constructor);
}); });
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends AbstractGearModel> GearModelKey<T> registerGear(GearModelKey<T> key, Wearable wearable, MsonModel.Factory<T> constructor) { static <T extends AbstractGear> GearModelKey<T> registerGear(GearModelKey<T> key, Wearable wearable, MsonModel.Factory<T> constructor) {
return (GearModelKey<T>)GEAR_MODELS.computeIfAbsent(wearable, w -> new GearModelKey<T>(key.key, constructor)); return (GearModelKey<T>)GEAR_MODELS.computeIfAbsent(wearable, w -> new GearModelKey<T>(key.key, constructor));
} }
@ -108,17 +122,17 @@ public final class ModelType {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Nullable @Nullable
public static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<E, T> getPlayerModel(Race race) { public static <E extends LivingEntity, T extends Model & MsonModel & IModel> PlayerModelKey<E, T> getPlayerModel(Race race) {
return (PlayerModelKey<E, T>)PLAYER_MODELS.get(race); return (PlayerModelKey<E, T>)PLAYER_MODELS.get(race);
} }
public static Stream<Map.Entry<Wearable, GearModelKey<? extends Gear>>> getWearables() { public static Stream<Map.Entry<Wearable, GearModelKey<? extends IGear>>> getWearables() {
return GEAR_MODELS.entrySet().stream(); return GEAR_MODELS.entrySet().stream();
} }
public static void bootstrap() { } public static void bootstrap() { }
public record GearModelKey<T extends Gear>(ModelKey<T> key, MsonModel.Factory<T> constructor) { public record GearModelKey<T extends IGear>(ModelKey<T> key, MsonModel.Factory<T> constructor) {
public T createModel() { public T createModel() {
return key.createModel(constructor); return key.createModel(constructor);
} }

View file

@ -1,4 +1,4 @@
package com.minelittlepony.api.model; package com.minelittlepony.client.model;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ArmorItem; import net.minecraft.item.ArmorItem;
@ -6,11 +6,12 @@ import net.minecraft.item.ItemStack;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.pony.PonyData; import com.minelittlepony.api.model.IModel;
import com.minelittlepony.client.model.PlayerModelKey; import com.minelittlepony.api.model.IModelWrapper;
import com.minelittlepony.client.model.armour.*; import com.minelittlepony.api.model.armour.*;
import com.minelittlepony.mson.api.ModelKey; import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.mson.api.MsonModel; import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.mson.api.*;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -18,14 +19,14 @@ import java.util.function.Consumer;
/** /**
* Container class for the various models and their associated piece of armour. * Container class for the various models and their associated piece of armour.
*/ */
public class Models<T extends LivingEntity, M extends PonyModel<?>> { public class ModelWrapper<T extends LivingEntity, M extends IModel> implements IModelWrapper {
@Nullable @Nullable
private final MsonModel.Factory<PonyArmourModel<T>> armorFactory; private final MsonModel.Factory<PonyArmourModel<T>> armorFactory;
private final Map<ModelKey<PonyArmourModel<?>>, PonyArmourModel<T>> armor = new HashMap<>(); private final Map<ModelKey<PonyArmourModel<?>>, PonyArmourModel<T>> armor = new HashMap<>();
private final M body; private final M body;
public Models(PlayerModelKey<T, ? super M> playerModelKey, boolean slimArms, @Nullable Consumer<M> initializer) { public ModelWrapper(PlayerModelKey<T, ? super M> playerModelKey, boolean slimArms, @Nullable Consumer<M> initializer) {
this.armorFactory = playerModelKey.armorFactory(); this.armorFactory = playerModelKey.armorFactory();
this.body = playerModelKey.getKey(slimArms).createModel(); this.body = playerModelKey.getKey(slimArms).createModel();
if (initializer != null) { if (initializer != null) {
@ -33,7 +34,7 @@ public class Models<T extends LivingEntity, M extends PonyModel<?>> {
} }
} }
public Models(ModelKey<M> key) { public ModelWrapper(ModelKey<M> key) {
this.armorFactory = null; this.armorFactory = null;
this.body = key.createModel(); this.body = key.createModel();
} }
@ -49,7 +50,8 @@ public class Models<T extends LivingEntity, M extends PonyModel<?>> {
})); }));
} }
public Models<T, M> applyMetadata(PonyData meta) { @Override
public ModelWrapper<T, M> applyMetadata(IPonyData meta) {
body.setMetadata(meta); body.setMetadata(meta);
armor.values().forEach(a -> a.setMetadata(meta)); armor.values().forEach(a -> a.setMetadata(meta));
return this; return this;

View file

@ -2,27 +2,31 @@ package com.minelittlepony.client.model;
import net.minecraft.client.model.Model; import net.minecraft.client.model.Model;
import net.minecraft.client.model.ModelPart; import net.minecraft.client.model.ModelPart;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.PlayerEntityRenderer;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.model.Models; import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.client.model.armour.PonyArmourModel; import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.mson.api.*; import com.minelittlepony.mson.api.*;
import java.util.function.*; import java.util.function.*;
public record PlayerModelKey<T extends LivingEntity, M extends Model & PonyModel<?>> ( public record PlayerModelKey<T extends LivingEntity, M extends Model & MsonModel & IModel> (
ModelKey<M> steveKey, ModelKey<M> steveKey,
ModelKey<M> alexKey, ModelKey<M> alexKey,
RendererFactory factory,
MsonModel.Factory<PonyArmourModel<T>> armorFactory MsonModel.Factory<PonyArmourModel<T>> armorFactory
) { ) {
PlayerModelKey(String name, BiFunction<ModelPart, Boolean, M> modelFactory, MsonModel.Factory<PonyArmourModel<T>> armorFactory) { PlayerModelKey(String name, BiFunction<ModelPart, Boolean, M> modelFactory, RendererFactory rendererFactory, MsonModel.Factory<PonyArmourModel<T>> armorFactory) {
this( this(
new ModelKeyImpl<>(new Identifier("minelittlepony", "races/steve/" + name), tree -> modelFactory.apply(tree, false)), 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)), new ModelKeyImpl<>(new Identifier("minelittlepony", "races/alex/" + name), tree -> modelFactory.apply(tree, true)),
rendererFactory,
armorFactory armorFactory
); );
} }
@ -31,12 +35,25 @@ public record PlayerModelKey<T extends LivingEntity, M extends Model & PonyModel
return slimArms ? alexKey : steveKey; return slimArms ? alexKey : steveKey;
} }
public <E extends T, N extends M> Models<E, N> create(boolean slimArms) { public <K extends T, N extends M> ModelWrapper<K, N> create(boolean slimArms) {
return create(slimArms, null); return create(slimArms, null);
} }
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public <E extends T, N extends M> Models<E, N> create(boolean slimArms, @Nullable Consumer<N> initializer) { public <K extends T, N extends M> ModelWrapper<K, N> create(boolean slimArms, @Nullable Consumer<N> initializer) {
return new Models(this, slimArms, initializer); return new ModelWrapper(this, slimArms, initializer);
}
@SuppressWarnings("unchecked")
public Function<EntityRendererFactory.Context, PlayerEntityRenderer> getFactory(boolean slimArms) {
return d -> factory.create(d, slimArms, (PlayerModelKey<AbstractClientPlayerEntity, ClientPonyModel<AbstractClientPlayerEntity>>)this);
}
public interface RendererFactory {
PlayerEntityRenderer create(
EntityRendererFactory.Context context,
boolean slim,
PlayerModelKey<AbstractClientPlayerEntity, ClientPonyModel<AbstractClientPlayerEntity>> key
);
} }
} }

View file

@ -13,6 +13,9 @@ import com.google.common.base.Strings;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.minelittlepony.api.config.PonyConfig; import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.model.armour.ArmourLayer;
import com.minelittlepony.api.model.armour.ArmourVariant;
import com.minelittlepony.api.model.armour.IArmourTextureResolver;
import com.minelittlepony.util.ResourceUtil; import com.minelittlepony.util.ResourceUtil;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -35,8 +38,8 @@ import java.util.concurrent.TimeUnit;
* - the "minecraft" namespace is always replaced with "minelittlepony" * - the "minecraft" namespace is always replaced with "minelittlepony"
* <p> * <p>
*/ */
public class ArmourTextureResolver { public class DefaultArmourTextureResolver implements IArmourTextureResolver {
public static final ArmourTextureResolver INSTANCE = new ArmourTextureResolver(); public static final DefaultArmourTextureResolver INSTANCE = new DefaultArmourTextureResolver();
private final Cache<String, Identifier> cache = CacheBuilder.newBuilder() private final Cache<String, Identifier> cache = CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.SECONDS) .expireAfterAccess(30, TimeUnit.SECONDS)
@ -46,6 +49,7 @@ public class ArmourTextureResolver {
cache.invalidateAll(); cache.invalidateAll();
} }
@Override
public Identifier getTexture(LivingEntity entity, ItemStack stack, EquipmentSlot slot, ArmourLayer layer, @Nullable String type) { public Identifier getTexture(LivingEntity entity, ItemStack stack, EquipmentSlot slot, ArmourLayer layer, @Nullable String type) {
Identifier material = stack.getItem() instanceof ArmorItem armor Identifier material = stack.getItem() instanceof ArmorItem armor
? new Identifier(armor.getMaterial().getName()) ? new Identifier(armor.getMaterial().getName())
@ -125,6 +129,7 @@ public class ArmourTextureResolver {
return MinecraftClient.getInstance().getResourceManager().getResource(texture).isPresent(); return MinecraftClient.getInstance().getResourceManager().getResource(texture).isPresent();
} }
@Override
public ArmourVariant getVariant(ArmourLayer layer, Identifier resolvedTexture) { public ArmourVariant getVariant(ArmourLayer layer, Identifier resolvedTexture) {
if (resolvedTexture.getPath().endsWith("_pony.png")) { if (resolvedTexture.getPath().endsWith("_pony.png")) {
return ArmourVariant.NORMAL; return ArmourVariant.NORMAL;

View file

@ -5,18 +5,20 @@ import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import com.minelittlepony.api.model.PonyModel; import com.minelittlepony.api.model.armour.*;
import com.minelittlepony.client.model.AbstractPonyModel; import com.minelittlepony.client.model.AbstractPonyModel;
import com.minelittlepony.client.model.IPonyModel;
public class PonyArmourModel<T extends LivingEntity> extends AbstractPonyModel<T> { public class PonyArmourModel<T extends LivingEntity> extends AbstractPonyModel<T> implements IArmourModel<T> {
public PonyArmourModel(ModelPart tree) { public PonyArmourModel(ModelPart tree) {
super(tree); super(tree);
} }
@Override
public boolean poseModel(T entity, float limbAngle, float limbDistance, float age, float headYaw, float headPitch, public boolean poseModel(T entity, float limbAngle, float limbDistance, float age, float headYaw, float headPitch,
EquipmentSlot slot, ArmourLayer layer, EquipmentSlot slot, ArmourLayer layer,
PonyModel<T> mainModel) { IPonyModel<T> mainModel) {
if (!setVisibilities(slot, layer)) { if (!setVisibilities(slot, layer)) {
return false; return false;

View file

@ -6,8 +6,6 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.mob.EndermanEntity; import net.minecraft.entity.mob.EndermanEntity;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import com.minelittlepony.api.pony.meta.Race;
public class EnderStallionModel extends SkeleponyModel<EndermanEntity> { public class EnderStallionModel extends SkeleponyModel<EndermanEntity> {
public boolean isCarrying; public boolean isCarrying;
@ -56,8 +54,8 @@ public class EnderStallionModel extends SkeleponyModel<EndermanEntity> {
} }
@Override @Override
public Race getRace() { public boolean canFly() {
return isAlicorn ? (super.getRace().hasHorn() ? Race.ALICORN : Race.PEGASUS) : super.getRace(); return isAlicorn;
} }
@Override @Override

View file

@ -1,16 +1,12 @@
package com.minelittlepony.client.model.entity; package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart; 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.render.entity.model.GuardianEntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.mob.GuardianEntity; import net.minecraft.entity.mob.GuardianEntity;
import com.minelittlepony.client.model.IPonyMixinModel;
import com.minelittlepony.api.model.PonyModelMixin;
import com.minelittlepony.client.model.entity.race.SeaponyModel; import com.minelittlepony.client.model.entity.race.SeaponyModel;
public class GuardianPonyModel extends GuardianEntityModel implements PonyModelMixin.Caster<GuardianEntity, SeaponyModel<GuardianEntity>, ModelPart> { public class GuardianPonyModel extends GuardianEntityModel implements IPonyMixinModel.Caster<GuardianEntity, SeaponyModel<GuardianEntity>, ModelPart> {
private final SeaponyModel<GuardianEntity> mixin; private final SeaponyModel<GuardianEntity> mixin;
public GuardianPonyModel(ModelPart tree) { public GuardianPonyModel(ModelPart tree) {
@ -22,25 +18,4 @@ public class GuardianPonyModel extends GuardianEntityModel implements PonyModelM
public SeaponyModel<GuardianEntity> mixin() { public SeaponyModel<GuardianEntity> mixin() {
return 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

@ -7,7 +7,7 @@ import net.minecraft.entity.mob.PiglinActivity;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import com.minelittlepony.api.model.ModelAttributes; import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.pony.Pony; import com.minelittlepony.api.pony.IPony;
public class PiglinPonyModel extends ZomponyModel<HostileEntity> { public class PiglinPonyModel extends ZomponyModel<HostileEntity> {
@ -23,7 +23,7 @@ public class PiglinPonyModel extends ZomponyModel<HostileEntity> {
} }
@Override @Override
public void updateLivingState(HostileEntity entity, Pony pony, ModelAttributes.Mode mode) { public void updateLivingState(HostileEntity entity, IPony pony, ModelAttributes.Mode mode) {
super.updateLivingState(entity, pony, mode); super.updateLivingState(entity, pony, mode);
leftArmPose = ArmPose.EMPTY; leftArmPose = ArmPose.EMPTY;
rightArmPose = entity.getMainHandStack().isEmpty() ? ArmPose.EMPTY : ArmPose.ITEM; rightArmPose = entity.getMainHandStack().isEmpty() ? ArmPose.EMPTY : ArmPose.ITEM;

View file

@ -8,10 +8,10 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.Arm; import net.minecraft.util.Arm;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import com.minelittlepony.api.model.MobPosingHelper; import com.minelittlepony.client.model.IMobModel;
import com.minelittlepony.client.model.entity.race.AlicornModel; import com.minelittlepony.client.model.entity.race.AlicornModel;
public class SkeleponyModel<T extends HostileEntity> extends AlicornModel<T> { public class SkeleponyModel<T extends HostileEntity> extends AlicornModel<T> implements IMobModel {
public boolean isUnicorn; public boolean isUnicorn;
@ -68,7 +68,7 @@ public class SkeleponyModel<T extends HostileEntity> extends AlicornModel<T> {
} }
protected void rotateArmHolding(ModelPart arm, float direction, float swingProgress, float ticks) { protected void rotateArmHolding(ModelPart arm, float direction, float swingProgress, float ticks) {
MobPosingHelper.rotateArmHolding(arm, direction, swingProgress, ticks); IMobModel.rotateArmHolding(arm, direction, swingProgress, ticks);
} }
@Override @Override
@ -78,7 +78,7 @@ public class SkeleponyModel<T extends HostileEntity> extends AlicornModel<T> {
@Override @Override
protected float getLegOutset() { protected float getLegOutset() {
if (attributes.isLyingDown) return 2.6f; if (attributes.isSleeping) return 2.6f;
if (attributes.isCrouching) return 0; if (attributes.isCrouching) return 0;
return 4; return 4;
} }

View file

@ -8,7 +8,7 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.RotationAxis;
import com.minelittlepony.api.model.ModelAttributes; import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.pony.Pony; import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.meta.*; import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.client.model.entity.race.EarthPonyModel; import com.minelittlepony.client.model.entity.race.EarthPonyModel;
@ -19,7 +19,7 @@ public class WitchPonyModel extends EarthPonyModel<WitchEntity> {
} }
@Override @Override
public void updateLivingState(WitchEntity entity, Pony pony, ModelAttributes.Mode mode) { public void updateLivingState(WitchEntity entity, IPony pony, ModelAttributes.Mode mode) {
super.updateLivingState(entity, pony, mode); super.updateLivingState(entity, pony, mode);
if (entity.hasCustomName() && "Filly".equals(entity.getCustomName().getString())) { if (entity.hasCustomName() && "Filly".equals(entity.getCustomName().getString())) {
@ -71,6 +71,9 @@ public class WitchPonyModel extends EarthPonyModel<WitchEntity> {
@Override @Override
public boolean isWearing(Wearable wearable) { public boolean isWearing(Wearable wearable) {
return wearable == Wearable.HAT || super.isWearing(wearable); if (wearable == Wearable.HAT) {
return true;
}
return super.isWearing(wearable);
} }
} }

View file

@ -1,12 +1,11 @@
package com.minelittlepony.client.model.entity; package com.minelittlepony.client.model.entity;
import com.minelittlepony.api.model.MobPosingHelper; import com.minelittlepony.client.model.IMobModel;
import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.client.model.entity.race.AlicornModel; import com.minelittlepony.client.model.entity.race.AlicornModel;
import net.minecraft.client.model.ModelPart; import net.minecraft.client.model.ModelPart;
import net.minecraft.entity.mob.HostileEntity; import net.minecraft.entity.mob.HostileEntity;
public class ZomponyModel<Zombie extends HostileEntity> extends AlicornModel<Zombie> { public class ZomponyModel<Zombie extends HostileEntity> extends AlicornModel<Zombie> implements IMobModel {
private boolean isPegasus; private boolean isPegasus;
@ -24,13 +23,13 @@ public class ZomponyModel<Zombie extends HostileEntity> extends AlicornModel<Zom
protected void rotateLegs(float move, float swing, float ticks, Zombie entity) { protected void rotateLegs(float move, float swing, float ticks, Zombie entity) {
super.rotateLegs(move, swing, ticks, entity); super.rotateLegs(move, swing, ticks, entity);
if (isZombified(entity)) { if (isZombified(entity)) {
MobPosingHelper.rotateUndeadArms(this, move, ticks); IMobModel.rotateUndeadArms(this, move, ticks);
} }
} }
@Override @Override
public Race getRace() { public boolean canFly() {
return isPegasus ? (super.getRace().hasHorn() ? Race.ALICORN : Race.PEGASUS) : super.getRace(); return isPegasus;
} }
protected boolean isZombified(Zombie entity) { protected boolean isZombified(Zombie entity) {

View file

@ -1,14 +1,14 @@
package com.minelittlepony.client.model.entity.race; package com.minelittlepony.client.model.entity.race;
import com.minelittlepony.api.model.SubModel; import com.minelittlepony.api.model.IPart;
import com.minelittlepony.api.model.WingedPonyModel; import com.minelittlepony.api.model.IPegasus;
import com.minelittlepony.client.model.part.PonyWings; import com.minelittlepony.client.model.part.PonyWings;
import com.minelittlepony.mson.api.ModelView; import com.minelittlepony.mson.api.ModelView;
import net.minecraft.client.model.ModelPart; import net.minecraft.client.model.ModelPart;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
public class AlicornModel<T extends LivingEntity> extends UnicornModel<T> implements WingedPonyModel<T> { public class AlicornModel<T extends LivingEntity> extends UnicornModel<T> implements IPegasus {
private PonyWings<AlicornModel<T>> wings; private PonyWings<AlicornModel<T>> wings;
@ -20,11 +20,11 @@ public class AlicornModel<T extends LivingEntity> extends UnicornModel<T> implem
public void init(ModelView context) { public void init(ModelView context) {
super.init(context); super.init(context);
wings = addPart(context.findByName("wings")); wings = addPart(context.findByName("wings"));
bodyRenderList.add(forPart(this::getWings).checked(() -> getRace().hasWings())); bodyRenderList.add(forPart(this::getWings).checked(this::canFly));
} }
@Override @Override
public SubModel getWings() { public IPart getWings() {
return wings; return wings;
} }
} }

View file

@ -12,12 +12,12 @@ public class ChangelingModel<T extends LivingEntity> extends AlicornModel<T> {
@Override @Override
public boolean wingsAreOpen() { public boolean wingsAreOpen() {
return (getAttributes().isFlying || getAttributes().isCrouching) && !getAttributes().isGliding; return (isFlying() || attributes.isCrouching) && !getAttributes().isGliding;
} }
@Override @Override
public float getWingRotationFactor(float ticks) { public float getWingRotationFactor(float ticks) {
if (getAttributes().isFlying) { if (isFlying()) {
return MathHelper.sin(ticks * 3) + WINGS_HALF_SPREAD_ANGLE; return MathHelper.sin(ticks * 3) + WINGS_HALF_SPREAD_ANGLE;
} }
return WINGS_RAISED_ANGLE; return WINGS_RAISED_ANGLE;

View file

@ -1,6 +1,6 @@
package com.minelittlepony.client.model.entity.race; package com.minelittlepony.client.model.entity.race;
import com.minelittlepony.api.model.SubModel; import com.minelittlepony.api.model.IPart;
import com.minelittlepony.client.model.AbstractPonyModel; import com.minelittlepony.client.model.AbstractPonyModel;
import com.minelittlepony.client.model.part.*; import com.minelittlepony.client.model.part.*;
import com.minelittlepony.mson.api.ModelView; import com.minelittlepony.mson.api.ModelView;
@ -12,7 +12,7 @@ public class EarthPonyModel<T extends LivingEntity> extends AbstractPonyModel<T>
private final boolean smallArms; private final boolean smallArms;
protected SubModel tail; protected IPart tail;
protected PonySnout snout; protected PonySnout snout;
protected PonyEars ears; protected PonyEars ears;

View file

@ -1,14 +1,14 @@
package com.minelittlepony.client.model.entity.race; package com.minelittlepony.client.model.entity.race;
import com.minelittlepony.api.model.SubModel; import com.minelittlepony.api.model.IPart;
import com.minelittlepony.api.model.WingedPonyModel; import com.minelittlepony.api.model.IPegasus;
import com.minelittlepony.client.model.part.PonyWings; import com.minelittlepony.client.model.part.PonyWings;
import com.minelittlepony.mson.api.ModelView; import com.minelittlepony.mson.api.ModelView;
import net.minecraft.client.model.ModelPart; import net.minecraft.client.model.ModelPart;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
public class PegasusModel<T extends LivingEntity> extends EarthPonyModel<T> implements WingedPonyModel<T> { public class PegasusModel<T extends LivingEntity> extends EarthPonyModel<T> implements IPegasus {
private PonyWings<PegasusModel<T>> wings; private PonyWings<PegasusModel<T>> wings;
@ -24,7 +24,7 @@ public class PegasusModel<T extends LivingEntity> extends EarthPonyModel<T> impl
} }
@Override @Override
public SubModel getWings() { public IPart getWings() {
return wings; return wings;
} }
} }

View file

@ -1,9 +1,9 @@
package com.minelittlepony.client.model.entity.race; package com.minelittlepony.client.model.entity.race;
import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.mson.api.ModelView; import com.minelittlepony.mson.api.ModelView;
import com.minelittlepony.api.model.*; import com.minelittlepony.api.model.*;
import com.minelittlepony.api.pony.Pony; import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.client.model.armour.PonyArmourModel;
import net.minecraft.client.model.ModelPart; import net.minecraft.client.model.ModelPart;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
@ -28,6 +28,8 @@ public class SeaponyModel<T extends LivingEntity> extends UnicornModel<T> {
leftPants.hidden = true; leftPants.hidden = true;
rightPants.hidden = true; rightPants.hidden = true;
leftSleeve.hidden = true;
rightSleeve.hidden = true;
leftLeg.hidden = true; leftLeg.hidden = true;
rightLeg.hidden = true; rightLeg.hidden = true;
} }
@ -45,7 +47,7 @@ public class SeaponyModel<T extends LivingEntity> extends UnicornModel<T> {
} }
@Override @Override
public void updateLivingState(T entity, Pony pony, ModelAttributes.Mode mode) { public void updateLivingState(T entity, IPony pony, ModelAttributes.Mode mode) {
super.updateLivingState(entity, pony, mode); super.updateLivingState(entity, pony, mode);
// Seaponies can't sneak, silly // Seaponies can't sneak, silly
@ -65,7 +67,7 @@ public class SeaponyModel<T extends LivingEntity> extends UnicornModel<T> {
float flapMotion = MathHelper.cos(ticks / 10) / 5; float flapMotion = MathHelper.cos(ticks / 10) / 5;
if (attributes.isLyingDown) { if (attributes.isSleeping) {
flapMotion /= 2; flapMotion /= 2;
} }
@ -73,7 +75,10 @@ public class SeaponyModel<T extends LivingEntity> extends UnicornModel<T> {
leftFin.yaw = finAngle; leftFin.yaw = finAngle;
rightFin.yaw = -finAngle; rightFin.yaw = -finAngle;
centerFin.roll = flapMotion;
if (!attributes.isSleeping) {
centerFin.roll = flapMotion;
}
if (!entity.isSubmergedInWater()) { if (!entity.isSubmergedInWater()) {
leftArm.pitch -= 0.5F; leftArm.pitch -= 0.5F;
@ -106,6 +111,11 @@ public class SeaponyModel<T extends LivingEntity> extends UnicornModel<T> {
super.transform(part, stack); super.transform(part, stack);
} }
@Override
public boolean hasMagic() {
return true;
}
@Override @Override
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
super.setVisible(visible); super.setVisible(visible);
@ -123,7 +133,7 @@ public class SeaponyModel<T extends LivingEntity> extends UnicornModel<T> {
} }
@Override @Override
public void updateLivingState(T entity, Pony pony, ModelAttributes.Mode mode) { public void updateLivingState(T entity, IPony pony, ModelAttributes.Mode mode) {
super.updateLivingState(entity, pony, mode); super.updateLivingState(entity, pony, mode);
// Seaponies can't sneak, silly // Seaponies can't sneak, silly

View file

@ -1,9 +1,9 @@
package com.minelittlepony.client.model.entity.race; package com.minelittlepony.client.model.entity.race;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.model.*; import com.minelittlepony.api.model.*;
import com.minelittlepony.api.pony.meta.Size; import com.minelittlepony.api.pony.meta.Size;
import com.minelittlepony.api.pony.meta.SizePreset; import com.minelittlepony.api.pony.meta.Sizes;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.client.model.part.UnicornHorn; import com.minelittlepony.client.model.part.UnicornHorn;
import com.minelittlepony.client.util.render.RenderList; import com.minelittlepony.client.util.render.RenderList;
import com.minelittlepony.mson.api.ModelView; import com.minelittlepony.mson.api.ModelView;
@ -16,7 +16,7 @@ import net.minecraft.util.*;
/** /**
* Used for both unicorns and alicorns since there's no logical way to keep them distinct and not duplicate stuff. * Used for both unicorns and alicorns since there's no logical way to keep them distinct and not duplicate stuff.
*/ */
public class UnicornModel<T extends LivingEntity> extends EarthPonyModel<T> implements HornedPonyModel<T> { public class UnicornModel<T extends LivingEntity> extends EarthPonyModel<T> implements IUnicorn {
protected final ModelPart unicornArmRight; protected final ModelPart unicornArmRight;
protected final ModelPart unicornArmLeft; protected final ModelPart unicornArmLeft;
@ -33,10 +33,10 @@ public class UnicornModel<T extends LivingEntity> extends EarthPonyModel<T> impl
public void init(ModelView context) { public void init(ModelView context) {
super.init(context); super.init(context);
horn = addPart(context.findByName("horn")); horn = addPart(context.findByName("horn"));
headRenderList.add(RenderList.of().add(head::rotate).add(forPart(horn)).checked(() -> getRace().hasHorn())); headRenderList.add(RenderList.of().add(head::rotate).add(forPart(horn)).checked(this::hasHorn));
this.mainRenderList.add(withStage(BodyPart.HEAD, RenderList.of().add(head::rotate).add((stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> { this.mainRenderList.add(withStage(BodyPart.HEAD, RenderList.of().add(head::rotate).add((stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
horn.renderMagic(stack, vertices, getAttributes().metadata.glowColor()); horn.renderMagic(stack, vertices, getMagicColor());
})).checked(() -> hasMagic() && isCasting())); })).checked(() -> hasHorn() && hasMagic() && isCasting()));
} }
@Override @Override
@ -57,7 +57,7 @@ public class UnicornModel<T extends LivingEntity> extends EarthPonyModel<T> impl
@Override @Override
public boolean isCasting() { public boolean isCasting() {
return PonyConfig.getInstance().tpsmagic.get() return MineLittlePony.getInstance().getConfig().tpsmagic.get()
&& (rightArmPose != ArmPose.EMPTY || leftArmPose != ArmPose.EMPTY); && (rightArmPose != ArmPose.EMPTY || leftArmPose != ArmPose.EMPTY);
} }
@ -70,7 +70,7 @@ public class UnicornModel<T extends LivingEntity> extends EarthPonyModel<T> impl
@Override @Override
public ModelPart getArm(Arm side) { public ModelPart getArm(Arm side) {
if (hasMagic() && getArmPoseForSide(side) != ArmPose.EMPTY && PonyConfig.getInstance().tpsmagic.get()) { if (hasMagic() && getArmPoseForSide(side) != ArmPose.EMPTY && MineLittlePony.getInstance().getConfig().tpsmagic.get()) {
return side == Arm.LEFT ? unicornArmLeft : unicornArmRight; return side == Arm.LEFT ? unicornArmLeft : unicornArmRight;
} }
return super.getArm(side); return super.getArm(side);
@ -80,7 +80,7 @@ public class UnicornModel<T extends LivingEntity> extends EarthPonyModel<T> impl
protected void positionheldItem(Arm arm, MatrixStack matrices) { protected void positionheldItem(Arm arm, MatrixStack matrices) {
super.positionheldItem(arm, matrices); super.positionheldItem(arm, matrices);
if (!PonyConfig.getInstance().tpsmagic.get() || !hasMagic()) { if (!MineLittlePony.getInstance().getConfig().tpsmagic.get() || !hasMagic()) {
return; return;
} }
@ -101,9 +101,9 @@ public class UnicornModel<T extends LivingEntity> extends EarthPonyModel<T> impl
float x = 0.3F; float x = 0.3F;
float z = -0.4F; float z = -0.4F;
if (size == SizePreset.TALL || size == SizePreset.YEARLING) { if (size == Sizes.TALL || size == Sizes.YEARLING) {
z += 0.05F; z += 0.05F;
} else if (size == SizePreset.FOAL) { } else if (size == Sizes.FOAL) {
x -= 0.1F; x -= 0.1F;
z -= 0.1F; z -= 0.1F;
} }

View file

@ -1,4 +1,4 @@
package com.minelittlepony.api.model.gear; package com.minelittlepony.client.model.gear;
import net.minecraft.client.model.Model; import net.minecraft.client.model.Model;
import net.minecraft.client.model.ModelPart; import net.minecraft.client.model.ModelPart;
@ -6,24 +6,22 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import com.minelittlepony.api.model.gear.IGear;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public abstract class AbstractGearModel extends Model implements Gear { public abstract class AbstractGear extends Model implements IGear {
private final List<ModelPart> parts = new ArrayList<>(); private final List<ModelPart> parts = new ArrayList<>();
private final float stackingHeight; public AbstractGear() {
public AbstractGearModel(float stackingHeight) {
super(RenderLayer::getEntitySolid); super(RenderLayer::getEntitySolid);
this.stackingHeight = stackingHeight;
} }
public AbstractGearModel addPart(ModelPart t) { public void addPart(ModelPart t) {
parts.add(t); parts.add(t);
return this;
} }
@Override @Override
@ -37,14 +35,4 @@ public abstract class AbstractGearModel extends Model implements Gear {
part.render(stack, renderContext, overlayUv, lightUv, red, green, blue, alpha); part.render(stack, renderContext, overlayUv, lightUv, red, green, blue, alpha);
}); });
} }
@Override
public boolean isStackable() {
return stackingHeight > 0;
}
@Override
public float getStackingHeight() {
return stackingHeight;
}
} }

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