Compare commits

...

127 commits

Author SHA1 Message Date
Sollace
01a0c82936
Merge branch '1.21.3' into 1.21.4 2024-12-15 21:12:15 +01:00
Sollace
58151e78c4
*punches gradle* Stop being dumb 2024-12-15 19:35:18 +01:00
Sollace
1e843e56fd
Fixed pony head textures 2024-12-15 19:26:23 +01:00
Sollace
bb456f9e83
Merge branch '1.21.3' into 1.21.4
# Conflicts:
#	src/main/java/com/minelittlepony/client/render/entity/feature/SkullFeature.java
2024-12-15 19:16:53 +01:00
Sollace
bb01cec530
Fixed positioning of worn skulls on foals 2024-12-15 19:13:41 +01:00
Sollace
d424629fdf
Remove unused import 2024-12-15 19:08:48 +01:00
Sollace
0298d6c938
Merge branch '1.21.3' into 1.21.4 2024-12-15 19:04:29 +01:00
Sollace
9ef39b936c
Merge branch '1.21' into 1.21.3
# Conflicts:
#	gradle.properties
#	src/main/java/com/minelittlepony/client/render/EquineRenderManager.java
#	src/main/java/com/minelittlepony/client/render/entity/feature/PassengerFeature.java
2024-12-15 18:57:34 +01:00
Sollace
5fce5b376c
Packport parrot sizing and fix parrots positioning 2024-12-15 17:19:37 +01:00
Sollace
7328d0c43d
Backport fixes for head position when riding/sitting 2024-12-15 17:19:18 +01:00
Sollace
7988da222f
Bump versions 2024-12-15 17:18:46 +01:00
Sollace
ca04c3aed0
Fix head position when riding more entity types (fixes compatibility with Sit mod and also mods where players can carry other players) 2024-12-15 15:00:55 +01:00
Sollace
787b166936
1.21.3 -> 1.21.4 2024-12-15 02:05:41 +01:00
Sollace
884fb62ff5
Duals skin mode is now the default mode 2024-12-14 22:43:19 +01:00
Sollace
5ff19ea93d
Clean up models a little and fix strider/spike models 2024-12-14 22:42:53 +01:00
Sollace
a3f71175cc
Ponify the bogged 2024-12-14 02:33:53 +01:00
Sollace
e922b397c8
Parrots render at the correct size when riding a pony's back 2024-12-14 00:54:37 +01:00
Sollace
2e7442b07a
Fix capes 2024-12-14 00:47:38 +01:00
Sollace
7fce6c94a3
Fix elytras 2024-12-13 22:52:20 +01:00
Sollace
e7cb03aa35
Fixed positioning of passengers (parrots) 2024-12-13 22:28:02 +01:00
Sollace
bcfed6cc84
Merge branch '1.21' into 1.21.3
# Conflicts:
#	gradle.properties
#	src/main/java/com/minelittlepony/client/transform/PonyTransformation.java
2024-12-13 22:09:36 +01:00
SKL
a99b3f861b
2 New Bodytypes + Minor Bugfix (#294)
- added stocky bodytype with the trigger pixel of #b2e7dd
- added SQUAT bodytype, based on "big daddy's" and 2 other background pony bodytypes
- fixed #289
2024-12-13 22:06:37 +01:00
Sollace
b9f3259a2c
Fixed eye position and model offsets when in a boat 2024-12-13 22:00:32 +01:00
Sollace
5728ce46bc
Fixed player skins 2024-12-13 17:32:21 +01:00
Sollace
eca325fd36
Fixed error rendering the player in the skin manager 2024-12-13 16:56:39 +01:00
Sollace
2cc222079b
Fixed incorrect leg angle 2024-12-13 16:52:03 +01:00
Sollace
6f85022038
Fixed crash when rendering the vex 2024-12-13 16:50:08 +01:00
Sollace
dc20a7f087
Fixed spyglass positioning 2024-12-13 16:45:33 +01:00
Sollace
dc34a8098a
Fixed wings not rendering 2024-12-13 16:17:40 +01:00
Sollace
2ace8f8f37
Fixed third person magic rendering 2024-12-13 15:56:28 +01:00
Sollace
4e57f6a267
Fixed armour stand rendering 2024-12-13 15:24:45 +01:00
Sollace
86fc15a59a
Simplify armor rendering a little 2024-12-13 14:25:44 +01:00
Sollace
4df9f5eece
Fix armour 2024-12-13 13:33:26 +01:00
Sollace
b7becaf3a4
Fixed player head rendering 2024-12-13 12:57:23 +01:00
Sollace
27281579fc
Update kirin and mson 2024-12-13 12:57:07 +01:00
Sollace
00e69317e9
Fix le bugs 2024-12-12 00:06:49 +01:00
Sollace
a02fad8732
Update models and fix crashes 2024-12-11 21:29:28 +01:00
Sollace
a71615d0a1
Bump hd skins 2024-11-27 14:54:48 +00:00
Sollace
add012294b
Bump hd skins 2024-11-27 14:54:14 +00:00
Sollace
f635d2e38f
More cleanup work 2024-11-22 17:22:41 +00:00
Sollace
3517e3150b
Cleanup mixins 2024-11-22 15:19:13 +00:00
Sollace
088175a862
Update mson and switch to using entity renderer conditions rather than re-registering when configs change 2024-11-22 15:18:59 +00:00
Sollace
f72fdaaebe
WIP Updating to 1.21.3 2024-11-21 23:26:35 +00:00
Sollace
1464b3318a
WIP Update to 1.21.3 2024-11-21 20:40:51 +00:00
Sollace
98abb4f384
Update dependencies 2024-11-21 20:40:28 +00:00
Sollace
0dc22d5c14
Update dependencies 2024-11-21 14:35:56 +00:00
Sollace
2035985c2f
WIP update to 1.21.3 2024-11-21 14:31:26 +00:00
Sollace
1b5a46b716
WIP update to 1.20.3 2024-11-09 15:51:59 +00:00
Sollace
c8709ea1b8
Fix crash on synatra connector due to mapping differences 2024-09-29 14:28:34 +01:00
Sollace
ce176ab2b5
Update plugins 2024-09-18 00:32:43 +01:00
Sollace
0e57e62f5a
Implement server-side functionality for getting pony data from a texture 2024-07-31 14:38:20 +02:00
Sollace
95b32fca23
Fix error when accessing the pony config in a server environment 2024-07-31 14:35:39 +02:00
Sollace
2fea9b9e55
Unsaved changes 2024-07-25 22:34:59 +02:00
Sollace
5a9d902d97
Added new bat pony parts models by ModernSilver1 2024-07-25 22:31:31 +02:00
Sollace
b3acab046e
Register networking response code when the mod is added to a server (also fix some client/server separation issues) 2024-07-25 16:44:49 +02:00
Sollace
55bf4e209f
Reload when the config is changed from the back 2024-07-25 15:59:26 +02:00
Sollace
eb25692aef
Fix naming convention for the logger 2024-07-25 15:45:14 +02:00
Sollace
36c3136067
Update kirin 2024-07-25 15:21:58 +02:00
Sollace
f4ea6d5ef0
Fix collission with other mods changing eye height/hitboxes 2024-07-23 15:01:17 +02:00
Sollace
8a801e7c41
Fixed reformed changelings' ears 2024-07-22 01:04:16 +02:00
Sollace
cc6cc074e8
Fixed camera height not updating immediately when loading a world 2024-07-22 00:25:24 +02:00
Sollace
90a5ac34fd
Fixed settings screen crash 2024-07-22 00:25:06 +02:00
Sollace
75f26ce061
Update and fix build 2024-07-22 00:20:05 +02:00
Sollace
5fd85964da
Bump dependencies 2024-06-10 22:07:16 +01:00
Sollace
d360ce397f
Fix startup crash 2024-06-10 21:09:58 +01:00
Sollace
86101e0807
Update gitmodules 2024-06-08 14:43:58 +01:00
Sollace
1d0001c702
Bump sockies 2024-06-08 14:42:42 +01:00
Sollace
ca2d21d77e
Add sockies as a plugin 2024-06-07 00:07:33 +01:00
Sollace
fa13d3263d
Bump smyp 2024-06-06 23:42:11 +01:00
Sollace
8db3536250
Update kirin 2024-06-06 23:35:56 +01:00
Sollace
d11fbf63f2
Update mixin 2024-06-06 23:00:49 +01:00
Sollace
7f89ef646e
Update dependencies and remove unneeded access wideners 2024-06-06 23:00:41 +01:00
Sollace
1ff9489a61
Bump hdskins 2024-06-04 23:59:23 +01:00
Sollace
91976a1020
1.20.5 -> 1.21-pre2 2024-06-04 23:43:55 +01:00
Sollace
8afa58f014
Deprecate moved classes 2024-06-04 22:41:56 +01:00
Sollace
20dda1d517
Update SMYP 2024-05-16 15:39:16 +01:00
­Sollace
49ae1f1f01
Update issue templates 2024-05-11 14:03:55 +01:00
­Sollace
a9ede9405f
Update issue templates 2024-05-11 13:56:12 +01:00
­Sollace
6bf9906795
Update issue templates 2024-05-11 13:54:08 +01:00
­Sollace
ed581e6dcc
Update issue templates 2024-05-11 13:52:11 +01:00
Sollace
70e8e23996
More work on magic glows to try and get them looking right 2024-05-07 01:28:57 +01:00
Sollace
2d2dc3b8d6
Bump hdskins version 2024-05-06 22:34:26 +01:00
Sollace
5524f8c7f1
Tools and weapons now keep their orientation when being held by a unicorn 2024-05-06 21:09:35 +01:00
Sollace
ff6b3d182d
Fix stetson 2024-05-06 20:41:11 +01:00
Sollace
ec193657af
Bump hdskins 2024-05-06 19:18:02 +01:00
Sollace
4963796c84
Fix leg position when lying down #259 2024-05-05 19:00:02 +01:00
Sollace
f0ee97bce0
Disable item glints when rendering the auras. Fixes #257 2024-05-05 18:32:57 +01:00
Sollace
1a22b290a6
Fixed hats, gear, stuck arrows, and etc not rendering on npcs 2024-05-05 18:21:04 +01:00
Sollace
0c0d77090f
Hats now move up so they don't clip through skulls/blocks 2024-05-05 18:20:36 +01:00
Sollace
411118d036
Pony skulls now play their animations 2024-05-05 18:02:12 +01:00
Sollace
c6560513bc
Fix misaligned skulls when worn by ponies + implement pony piglin skulls 2024-05-05 17:51:01 +01:00
Sollace
405d9e2bbf
Fix build tasks 2024-05-05 00:43:25 +01:00
Sollace
a1e35bedb6
Bump plugin ref 2024-05-05 00:36:15 +01:00
Sollace
9c103dbddd
Apply alpha for worn heads 2024-05-05 00:34:15 +01:00
Sollace
aac343cd46
Add a callback after rendering armour 2024-05-05 00:11:15 +01:00
Sollace
c4968aed4c
Make sure getArmourStacks is called whenever possible 2024-05-04 23:50:17 +01:00
Sollace
4b6fdd4752
Clean up and simplify a little 2024-05-04 23:23:57 +01:00
Sollace
bf4a0139fb
Add support for the cape and elytra 2024-05-04 23:02:57 +01:00
Sollace
53b8921c50
Update gitmodules 2024-05-04 22:08:04 +01:00
Sollace
0da671b699
Clean up a bit and allow for setting alpha 2024-05-04 22:03:35 +01:00
Sollace
93a895e90e
Add a plugin for show me your skins 2024-05-04 21:38:38 +01:00
Sollace
53c9a85877
Allow for chaining plugins when registering 2024-05-04 21:36:58 +01:00
Sollace
ced4a2d980
Implement a armour renderplugin api 2024-05-04 21:21:16 +01:00
Sollace
85726e8c8d
Fix fillycam 2024-05-04 19:43:24 +01:00
Sollace
eeea8ee184
Fix turtle helmet and clear cached armour lookups when reloading resources 2024-05-04 19:32:05 +01:00
Sollace
3c44b00c1d
Update armour rendering to use the armor material layers defined by the item.
Also fixes #269, #270
2024-05-04 00:03:28 +01:00
Sollace
8d385a3202
Skeletons now lower their bow when not attacking. ;) 2024-04-30 19:11:48 +01:00
Sollace
a9051293b7
Fixed magic rendering on items in third person when it's not supposed to and fixed skeletons having horns when they're not supposed to 2024-04-30 19:11:32 +01:00
Sollace
62d606ada4
Update skin type ids and implement apis for controlling what skin and model the player gets through mine little pony 2024-04-30 19:11:04 +01:00
Sollace
cc463bb293
Fix riding position, and remove pony stacking (it was broken and I can't fix it) 2024-04-30 17:04:59 +01:00
Sollace
807fb3213a
1.20.4 -> 1.20.5 2024-04-30 15:45:36 +01:00
Sollace
36702e1fec
Merge branch '1.20.2' into 1.20.4 2024-04-26 21:04:39 +01:00
Sollace
1fc533babd
New Crowdin updates (#273)
# Conflicts:
#	src/main/resources/assets/minelittlepony/lang/ru_ru.json
2024-04-09 14:20:50 +01:00
­Sollace
4a3b6bb54b
Update Crowdin configuration file 2024-04-09 14:20:46 +01:00
­Sollace
30e899a7e0
Update README.md 2024-04-09 13:32:52 +01:00
­Sollace
1bab1fe79c
Update Crowdin configuration file 2024-04-09 13:32:41 +01:00
Sollace
fce1db2cc7
Update dependencies 2024-04-06 15:19:37 +01:00
Sollace
e8035e97f5
Merge branch '1.20.2' into 1.20.4 2024-03-05 12:26:19 +00:00
Sollace
8aa189f928
Fixed not all wearables being detected 2024-03-05 12:16:28 +00:00
Sollace
50e83bb930
Add mson to the depends block (almost forgot this) 2024-03-05 12:16:28 +00:00
Sollace
58076df09a
Fix various transformations for when a pony is lying down but not sleeping 2024-03-05 12:14:12 +00:00
Sollace
7ad3ab236a
Increase time required before a pony will lie down 2024-03-05 12:14:12 +00:00
横刀天笑(Knife smile)
8687ec443b
update skin form (#262) 2024-02-23 21:45:58 +00:00
Sollace
709879fe61
Update mson 2024-02-09 19:42:41 +00:00
Sollace
9f33f4c06a
Fix typo 2024-01-09 16:44:28 +01:00
Sollace
daa40cc143
Actually use the dynamic depends blocks 2023-12-09 23:33:38 +00:00
Sollace
63ec053bc2
1.20.2 -> 1.20.4 2023-12-09 22:51:59 +00:00
240 changed files with 5394 additions and 4357 deletions

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

8
.gitmodules vendored
View file

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

File diff suppressed because one or more lines are too long

BIN
assets/night_guard_bat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

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

View file

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

View file

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

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

1
plugins/Sockies Submodule

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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