Compare commits

...

167 commits

Author SHA1 Message Date
Sollace
57754d8dec
Change christmas check to include the 24th - 26th Closes #299 2025-01-05 12:28:59 +01:00
Sollace
ec8f92e151
Update hdskins
# Conflicts:
#	gradle.properties
2024-12-17 17:59:46 +01:00
Sollace
e21ce62631
Merge branch '1.20.1' into 1.20.2 2024-07-31 15:13:32 +02:00
Sollace
c78cf32bd4
Fix build 2024-07-31 15:13:14 +02:00
Sollace
4ef2d4ab54
Merge branch '1.20.1' into 1.20.2
# Conflicts:
#	src/main/java/com/minelittlepony/client/model/part/PonyEars.java
2024-04-26 21:02:12 +01:00
Sollace
82a89eb1ba
Apply changeling pony ears fix from 1.20.2+ 2024-04-26 21:01:08 +01:00
Sollace
8bf3bc38a4
Remove patch that only applies to 1.20.1 2024-04-26 20:59:39 +01:00
Sollace
e01580f804
Merge branch '1.20.1' into 1.20.2 2024-04-26 20:55:50 +01:00
­Sollace
040105c792
New Crowdin updates (#275)
* New translations en_us.json (English (upside down))
* New translations en_us.json (Pirate English)
* New translations en_us.json (English, United Kingdom)
* New translations en_us.json (LOLCAT)
* New translations en_us.json (Anglish)
* New translations en_us.json (German)
* New translations en_us.json (Afrikaans)
2024-04-26 20:38:00 +01:00
­Sollace
18591c0720
New Crowdin updates (#274)
* New translations en_us.json (German)

* New translations en_us.json (Russian)
2024-04-09 16:07:34 +01:00
Sollace
31a9a9b0e6
New Crowdin updates (#273)
# Conflicts:
#	src/main/resources/assets/minelittlepony/lang/ru_ru.json
2024-04-09 14:19:51 +01:00
­Sollace
c46cbea872
Update Crowdin configuration file 2024-04-09 14:19:03 +01:00
­Sollace
299a81088c
New Crowdin updates (#273) 2024-04-09 14:17:19 +01:00
­Sollace
bdc807b102 Update Crowdin configuration file 2024-04-09 14:08:12 +01:00
­Sollace
e149e926d2
Update README.md 2024-04-09 13:32:07 +01:00
­Sollace
4d55f546bb
Update Crowdin configuration file 2024-04-09 13:32:01 +01:00
­Sollace
c0e4620355
Update README.md 2024-04-09 13:31:18 +01:00
­Sollace
a3ea4f5982 Update Crowdin configuration file 2024-04-09 13:25:43 +01:00
Sollace
39f4a7d26d
Update dependencies 2024-04-06 15:17:02 +01:00
Sollace
71518d9478
Update dependencies 2024-04-06 15:12:19 +01:00
Sollace
dfa9ce8977
Fix giant scale 2024-03-05 12:25:36 +00:00
Sollace
0d97f6b9d4
Fix giant scale 2024-03-05 12:24:54 +00:00
Sollace
76211930d5
Fixed not all wearables being detected 2024-03-05 12:10:15 +00:00
Sollace
a40b55e142
Fix the leg issue 2024-02-24 16:36:03 +00:00
Sollace
aa08f12320
Add mson to the depends block (almost forgot this) 2024-02-09 19:47:31 +00:00
Sollace
f3cc92dee6
Fix typo 2024-02-09 19:45:42 +00:00
Sollace
9c8d6d0a86
Actually use the dynamic depends blocks 2024-02-09 19:45:34 +00:00
Sollace
8ce2fc4e6d
Update mson 2024-02-09 19:39:12 +00:00
Sollace
7da44b879c
Fix various transformations for when a pony is lying down but not sleeping 2024-02-09 19:34:10 +00:00
Sollace
f5bfa14bb1
Increase time required before a pony will lie down 2024-02-09 19:34:05 +00:00
Sollace
d392b204a3
Update mson 2024-02-09 19:32:22 +00:00
Sollace
21a33ebc76
Temp fix for an hdskins bug 2024-02-09 19:25:44 +00:00
Sollace
0895e94053
Fix various transformations for when a pony is lying down but not sleeping 2024-02-09 19:25:23 +00:00
Sollace
387c9307d0
Increase time required before a pony will lie down 2024-02-09 19:24:49 +00:00
Sollace
475e96726e
Update mson 2024-02-09 17:40:15 +00:00
Sollace
9d65185fcd
Update skins pack 2024-02-09 16:57:40 +00:00
Sollace
c365c141ef
Update mson one last time 2023-12-08 17:23:42 +00:00
Sollace
a94137fda9
1.20.2-rc1 -> 1.20.2 2023-12-08 17:23:34 +00:00
Sollace
34087bd105
Update mson to fix sodium compatibility 2023-12-07 21:49:33 +00:00
Sollace
cb423186e7
Fix reformed changelings' ears 2023-12-07 21:48:10 +00:00
Sollace
af99765bf2
Fix lighting on pony butts 2023-12-07 21:47:59 +00:00
Sollace
1faa192f33
Add nirik skin type 2023-12-07 21:47:49 +00:00
Sollace
b9baba84de
Update skin pack 2023-12-07 21:47:22 +00:00
Sollace
814dac6414
Fix seapony skins. Might have been a simpler issue than I thought 2023-12-06 15:18:39 +00:00
Sollace
667ce0ce4b
Parse pony data on the client thread executor, and don't send unparsed pony data responses to a consenting server. Rather wait for the data to resolve and let the entity send it normally.
# Conflicts:
#	src/main/java/com/minelittlepony/api/events/Channel.java
#	src/main/java/com/minelittlepony/client/util/render/NativeUtil.java
2023-10-29 21:39:25 +00:00
Sollace
8832beac32
Parse pony data on the client thread executor, and don't send unparsed pony data responses to a consenting server. Rather wait for the data to resolve and let the entity send it normally. 2023-10-27 22:09:55 +01:00
Sollace
4de3f3daa2
Fix constant logging when playing on a multiplayer server that doesn't register for receiving pony data packets
# Conflicts:
#	src/main/java/com/minelittlepony/api/events/Channel.java
#	src/main/java/com/minelittlepony/client/render/EquineRenderManager.java
2023-10-24 01:16:53 +01:00
Sollace
9064272771
Fix constant logging when playing on a multiplayer server that doesn't register for receiving pony data packets 2023-10-24 01:12:36 +01:00
Sollace
011ceedafe
Update version range 2023-10-06 19:33:46 +01:00
Sollace
2ed7f91786
Only replace a layer if it has a compatible buffer format. Should fix #254 2023-10-06 18:51:45 +01:00
Sollace
d011c11689
Only replace a layer if it has a compatible buffer format. Should fix #254 2023-10-06 18:51:20 +01:00
Sollace
060c4abfff
Ponies can now lie down by holding crouch and not moving 2023-10-06 00:48:11 +01:00
Sollace
9f77999baf
Fixed tiny silly ponies reverting to normal villagers when they grow up
# Conflicts:
#	src/main/java/com/minelittlepony/client/render/entity/npc/textures/SillyPonyTextureSupplier.java
2023-10-06 00:47:45 +01:00
Sollace
fa0c903c0f
Fixed magic not rendering for items held by other entities 2023-10-06 00:45:48 +01:00
Sollace
a7acd2a1c1
More fixes 2023-10-06 00:45:19 +01:00
Sollace
101b2bd20c
Fixed mobs' arms sticking too far up when sitting in a boat
# Conflicts:
#	src/main/java/com/minelittlepony/client/model/IMobModel.java
2023-10-06 00:40:04 +01:00
Sollace
9334836aa7
Disable shadow position changing
# Conflicts:
#	src/main/java/com/minelittlepony/client/render/entity/PlayerPonyRenderer.java
2023-10-06 00:38:52 +01:00
Sollace
a69140b761
fixes 2023-10-06 00:38:46 +01:00
Sollace
50ce9f92d1
Fixed left ear rotating incorrectly 2023-10-06 00:36:50 +01:00
Sollace
971f3623cf
Fixed pony skin data not being reliably sent to servers
# Conflicts:
#	src/main/java/com/minelittlepony/api/pony/network/fabric/Channel.java
#	src/main/java/com/minelittlepony/client/compat/hdskins/DummyPony.java
#	src/main/java/com/minelittlepony/client/mixin/MixinClientPlayerEntity.java
#	src/main/java/com/minelittlepony/client/render/EquineRenderManager.java
2023-10-06 00:35:33 +01:00
Sollace
24e220bb29
Allow armor to render with translucency 2023-10-06 00:23:08 +01:00
Sollace
f8b21e78ba
Update settings.gradle 2023-10-06 00:20:19 +01:00
Sollace
131bc7cb3f
Fixed armour models loading from the incorrect directory 2023-10-06 00:20:05 +01:00
Sollace
31857e2de0
The direction a pony's head faces when lying in a bed is now determined by which way you're looking when you climb in 2023-10-06 00:19:42 +01:00
Sollace
d3a5c80981
Fixed trident animation. Closes #250 2023-10-06 00:18:09 +01:00
Sollace
be5382ebad
Fixed bat ponies' wings rotating incorrectly and move them up a bit 2023-10-06 00:17:58 +01:00
Sollace
3ae6a48204
Fixed horns not glowing 2023-10-06 00:17:01 +01:00
Sollace
4717c9e458
Ponies can now lie down by holding crouch and not moving 2023-10-05 20:42:31 +01:00
Sollace
e31ee1c53b
Fixed tiny silly ponies reverting to normal villagers when they grow up 2023-10-05 20:28:29 +01:00
Sollace
509f6477f2
Fixed magic not rendering for items held by other entities 2023-10-05 20:22:09 +01:00
Sollace
3322e7eaff
Fixed model visibility not updating for mobs 2023-10-05 20:21:50 +01:00
Sollace
4904c78e01
Fixed mob placement in boats 2023-10-05 20:05:07 +01:00
Sollace
8dad366317
Fixed mobs' arms sticking too far up when sitting in a boat 2023-10-05 20:04:55 +01:00
Sollace
b1869c824e
Disable shadow position changing 2023-10-05 19:53:14 +01:00
Sollace
72ff58015d
Fixed left ear rotating incorrectly 2023-10-05 19:52:03 +01:00
Sollace
76a2e043b7
Clean up the EquineRendermanager (should fix some crashing issues with null seapony models) 2023-10-05 19:46:40 +01:00
横刀天笑(Knife smile)
f54cd5682d
update zh_cn.json (#253) 2023-09-28 14:40:34 +01:00
Sollace
26aa16d684
Fixed pony skin data not being reliably sent to servers 2023-09-28 14:38:39 +01:00
Sollace
9f280f79eb
Move SkinsProxy to the api package and decouple from the implementations a little better 2023-09-28 14:07:36 +01:00
Sollace
b538174933
Clean up the code a little bit 2023-09-28 13:48:52 +01:00
Sollace
f70ec0996c
Fixed buffer overflow 2023-09-28 13:48:14 +01:00
Sollace
c2337058f6
Update russian translation 2023-09-27 17:41:20 +01:00
Sollace
1c4fa62676
Update translations 2023-09-27 17:38:32 +01:00
Sollace
f5603b05b7
Update dependencies 2023-09-27 17:28:30 +01:00
Sollace
ca728ded81
Allow armor to render with translucency 2023-09-27 12:11:20 +01:00
Sollace
24a8afc508
Change mixed humans skins option to a dual skin mode and add a priority trigger pixel to facilitate skin sorting 2023-09-27 02:00:18 +01:00
Sollace
6013fa2ad5
Add experimental option to skip hd skins when showing players as humans 2023-09-27 01:27:20 +01:00
Sollace
2c6c23d05c
Fix typo 2023-09-27 01:18:26 +01:00
Sollace
0f9e865dc4
Update hdskins 2023-09-26 21:13:16 +01:00
Sollace
15274aba2b
Fixed settings screen background not rendering correctly 2023-09-26 20:22:52 +01:00
Sollace
429cdb644e
Update kirin 2023-09-26 20:22:38 +01:00
Sollace
5cf082d28d
Update settings.gradle 2023-09-26 19:56:27 +01:00
Sollace
dcb8631eb9
The direction a pony's head faces when lying in a bed is now determined by which way you're looking when you climb in 2023-09-26 17:57:18 +01:00
Sollace
f99309cd55
Fixed trident animation. Closes #250 2023-09-26 17:14:09 +01:00
Sollace
5b9d85d8ea
Update hdskins 2023-09-26 16:45:35 +01:00
Sollace
cd25b9e269
Fixed bat ponies' wings rotating incorrectly and move them up a bit 2023-09-26 16:45:25 +01:00
Sollace
b37ffa9860
Rewrites round tri 2023-09-26 16:42:55 +01:00
Sollace
0e30b2c8dd
Fix that pesky compiler warning 2023-09-26 01:45:50 +01:00
Sollace
4b842c9e9a
Further refactoring and consolidating 2023-09-26 01:45:28 +01:00
Sollace
a9b870547a
Api version gets incremented. The size preceding wearables was changed to a varint. 2023-09-25 21:08:24 +01:00
Sollace
0d226b51f9
Rewrite pretty much everything to make more effective use of java's Records 2023-09-25 21:07:09 +01:00
Sollace
8eca1796e1
Convert PonyData classes into records 2023-09-25 02:55:43 +01:00
Sollace
ff800006f7
Fixed horns not glowing 2023-09-25 01:56:15 +01:00
Sollace
ba0ab4149e
Fixed slim arm textures being applied to the thick arm models 2023-09-25 01:42:00 +01:00
Sollace
e45c237e82
Fixed incorrect riding position 2023-09-25 01:41:43 +01:00
Sollace
627adab1f6
Fixed seapony previews in the skinning gui 2023-09-25 01:41:30 +01:00
Sollace
9a59f2444e
Anyone can now use the seapony skin variant 2023-09-25 00:49:38 +01:00
Sollace
47adfd5d83
1.20.1 -> 1.20.2 2023-09-24 23:43:31 +01:00
Sollace
d346f7f60d
Remove log message 2023-08-27 19:36:44 +01:00
Sollace
91918bab0c
Improve seapony tail animations 2023-08-21 21:25:04 +01:00
Sollace
72004812f2
Remove the seapony splashing 2023-08-21 21:24:50 +01:00
Sollace
da790fc984
Fixed several model issues so they output correctly when converted to bbmodel files 2023-08-21 21:24:36 +01:00
Sollace
5a0d3a073d
Fixed glitchy magic glows with vulkan mod 2023-08-19 22:42:35 +01:00
Sollace
db75aa1600
Shortcircuit image loading to use an existing NativeImage if one is available 2023-08-19 22:14:36 +01:00
Sollace
0096dec423
Fixed vulkan mod crash 2023-08-19 22:14:01 +01:00
Sollace
7c099cd0b1
Add some improvements to seaponies 2023-08-01 22:46:36 +01:00
Sollace
b4632c7358
Increase cuteness factor 2023-08-01 22:05:43 +01:00
Sollace
4db4a239bd
Fixed missing hand animations for tooting horns and brushing brushes 2023-08-01 22:05:27 +01:00
Sollace
5de74f27f3
Fixed inverted textures on the clothing layer 2023-08-01 22:04:46 +01:00
Sollace
4d98160738
Update background pony skins ref 2023-08-01 22:04:22 +01:00
Sollace
0aee3343f8
Fix for Traben-0/Entity_Model_Features#89 (because apparently we can't trust the context to give us the model we set) 2023-08-01 20:26:38 +01:00
­Sollace
262a39e1d8
Add modrinth badge 2023-07-24 21:41:16 +01:00
Sollace
be474aa579
Fixed trigger pixels legend 2023-07-24 20:53:38 +01:00
Sollace
ab84884677
Update HDSkins 2023-07-24 20:41:09 +01:00
Sollace
8042e83e8a
Fixed neck appearing in first person when First Person Model is installed 2023-07-10 20:01:24 +01:00
Sollace
c2380013a9
Fixed right claw texture on gryphons 2023-07-10 19:43:26 +01:00
Sollace
528a527e36
Update background ponies list 2023-07-10 19:43:15 +01:00
Sollace
8d24807ae0
Fixed tooltip not rendering for the last legend block 2023-07-10 19:29:10 +01:00
­Sollace
d6f8df66e8
Merge pull request #248 from Fauli1221/lang_patch
Fixed incorrect translation strings for the skin types in other languages
2023-07-10 18:07:41 +01:00
Azure Star
23d0bd131b
Fixed incorrect translation strings for the skin types in other languages that translate them 2023-07-10 08:08:28 +02:00
Sollace
385d09e0f5
Added some more guardian variants 2023-07-09 21:30:14 +01:00
Sollace
891aa73254
Fixed guardians not rendering correctly 2023-07-09 21:12:32 +01:00
Sollace
3fd07b4874
Fixed items not being positioned correctly in seaponys' hooves. Closes #239 2023-07-09 21:12:15 +01:00
Sollace
b6a6adbbf6
Bump hdskins version 2023-07-09 19:29:19 +01:00
Sollace
19f4a4781e
Fixed flipped back texture 2023-07-09 19:29:07 +01:00
Sollace
5a794ed611
Remove debug code (again) 2023-07-09 19:28:55 +01:00
Sollace
bee3060697
Fixed seapony transformation effect not playing in first person mode + clothing layer not updating 2023-07-09 17:41:30 +01:00
Sollace
3aabd80e5b
Preserve the render layer used by item glints 2023-07-09 16:36:00 +01:00
Sollace
b38c8530f8
Use the correct texture in first person mode. Closes #238 2023-07-09 16:15:59 +01:00
Sollace
ee536cfe6a
Update dependencies 2023-07-09 16:15:37 +01:00
Sollace
c7c5e588c2
Fixed alex arm placement. Closes #242 2023-07-09 16:15:18 +01:00
Sollace
43c6375e43
Fixed incompatibility with EntityModelFeatures 2023-07-09 15:13:30 +01:00
Sollace
3f76f6057e
Fixed incorrect translation strings for the skin types 2023-07-09 15:13:09 +01:00
Sollace
73dae14934
1.20pre6 -> 1.20.1 / update dependencies 2023-07-09 14:49:22 +01:00
Sollace
f433f7d66f
Update kirin 2023-07-06 13:27:57 +01:00
Sollace
7f9bdcbde5
Fixed crash when trying to render capes whilst Entity Model Features is installed 2023-07-01 21:43:58 +01:00
Sollace
471b2f01a0
Fixed inverted seapony wing 2023-07-01 21:43:21 +01:00
Sollace
b842f3d4df Revert reckon 2023-06-06 20:24:46 +01:00
Sollace
ef83ca820f Update loom and reckon 2023-06-02 11:27:21 +01:00
­Sollace
a1d15e4da2
Update readme: show release version instead of tag version 2023-05-30 20:25:45 +01:00
­Sollace
5f7fe72d28
Update README.md 2023-05-30 20:23:02 +01:00
Sollace
031628d0ce Update kirin 2023-05-30 19:58:50 +01:00
Sollace
b7a15cda83 Update mson 2023-05-29 20:35:44 +01:00
Sollace
dad8f83718 1.19.4 -> 1.20-pre6 2023-05-29 20:26:00 +01:00
Sollace
f1b6bb23a7 Fixed hippogriffs not using the correct models 2023-04-11 16:58:44 +02:00
Sollace
cb232cc145 Remove redundant magic rendering code 2023-04-10 22:46:18 +02:00
Sollace
437d361170 Fix enderman legs 2023-04-10 17:30:07 +02:00
Sollace
2e5fec3106 Fix spyglass placement and third person orientation of items in unicorn's auras 2023-04-10 17:04:53 +02:00
Sollace
d97403fd26 Improved compatibility with not enough animations (maps) #235 2023-04-10 16:57:54 +02:00
Sollace
77584ea2fa Fixed levitating item placement in third person and fixed crowssbow positioning 2023-04-10 16:55:41 +02:00
Sollace
0881cf6e5b Seapony skins now work for hippogriffs 2023-04-10 14:09:12 +02:00
Sollace
e55b17f3a7 Fixed body parts not being properly hidden/made visible again by the game when clothing is enabled. Fixes #236 2023-04-10 14:08:55 +02:00
Sollace
d438d0f5bb Fixed armour crash 2023-04-01 23:35:41 +01:00
Sollace
fb3973a3f1 Baby zombie ponies have a small chance of spawning as cozy glow 2023-04-01 21:44:21 +01:00
Sollace
d9769e32ee April fools 2023 2023-04-01 21:36:45 +01:00
Sollace
32d1ffbe7c Revert "Remove another setVisible(true) call"
This reverts commit fadbea94d8.
2023-03-29 21:35:44 +01:00
Sollace
b2fe56334f Update some parameter names 2023-03-29 21:35:01 +01:00
242 changed files with 3757 additions and 3680 deletions

View file

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

View file

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

3
crowdin.yml Normal file
View file

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

View file

@ -3,10 +3,10 @@ org.gradle.daemon=false
# Fabric Properties # Fabric Properties
# check these on https://fabricmc.net/develop # check these on https://fabricmc.net/develop
minecraft_version=1.19.4 minecraft_version=1.20.2
yarn_mappings=1.19.4+build.1 yarn_mappings=1.20.2+build.4
loader_version=0.14.17 loader_version=0.15.1
fabric_version=0.76.0+1.19.4 fabric_version=0.91.1+1.20.2
# Mod Properties # Mod Properties
group=com.minelittlepony 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. description=Mine Little Pony turns players and mobs into ponies. Press F9 ingame to access settings.
# Publishing # Publishing
minecraft_version_range=>=1.19.4 minecraft_version_range=>=1.20.2
modrinth_loader_type=fabric modrinth_loader_type=fabric
modrinth_project_id=JBjInUXM modrinth_project_id=JBjInUXM
# Dependencies # Dependencies
modmenu_version=6.1.0-rc.4 modmenu_version=8.0.0
kirin_version=1.14.0 kirin_version=1.16.1+1.20.2
hd_skins_version=6.8.0 hd_skins_version=6.12.4+1.20.2
mson_version=1.8.0 mson_version=1.9.3+1.20.2

View file

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

View file

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

2
skins

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

View file

@ -2,8 +2,8 @@ package com.minelittlepony.api.config;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import com.minelittlepony.api.pony.meta.Race; import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.api.pony.meta.Sizes; import com.minelittlepony.common.client.gui.VisibilityMode;
import com.minelittlepony.common.util.GamePaths; import com.minelittlepony.common.util.GamePaths;
import com.minelittlepony.common.util.settings.*; import com.minelittlepony.common.util.settings.*;
@ -52,8 +52,15 @@ public class PonyConfig extends Config {
public final Setting<Boolean> frustrum = value("settings", "frustrum", true) public final Setting<Boolean> frustrum = value("settings", "frustrum", true)
.addComment("Adjust camera intersection checks to properly cull entities when they're not in view.") .addComment("Adjust camera intersection checks to properly cull entities when they're not in view.")
.addComment("Helps to prevent entities from vanishing when they're in long stacks"); .addComment("Helps to prevent entities from vanishing when they're in long stacks");
public final Setting<Boolean> horsieMode = value("settings", "horsieMode", false)
.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<Sizes> sizeOverride = value("debug", "sizeOverride", Sizes.UNSET) public final Setting<SizePreset> sizeOverride = value("debug", "sizeOverride", SizePreset.UNSET)
.addComment("Overrides pony sizes") .addComment("Overrides pony sizes")
.addComment("Possible values: TALL, BULKY, LANKY, NORMAL, YEARLING, FOAL, UNSET (default)"); .addComment("Possible values: TALL, BULKY, LANKY, NORMAL, YEARLING, FOAL, UNSET (default)");
@ -67,10 +74,17 @@ public class PonyConfig extends Config {
public final Setting<Boolean> flappyElytras = value("customisation", "flappyElytras", false) public final Setting<Boolean> flappyElytras = value("customisation", "flappyElytras", false)
.addComment("Pegasi will use their wings to fly even when they're wearing an elytra"); .addComment("Pegasi will use their wings to fly even when they're wearing an elytra");
public final Setting<Boolean> noFun = value("customisation", "noFun", false) public final Setting<Boolean> noFun = value("customisation", "noFun", false)
.addComment("Disables certain easter eggs and secrets (party pooper)") .addComment("Disables certain easter eggs and secrets (party pooper)")
.addComment("Turning this off may help with compatibility in some cases"); .addComment("Turning this off may help with compatibility in some cases");
public final Setting<VisibilityMode> horseButton = value("horseButton", VisibilityMode.AUTO)
.addComment("Whether to show the mine little pony settings button on the main menu")
.addComment("AUTO (default) - only show when HDSkins is not installed")
.addComment("ON - always show")
.addComment("OFF - never show");
public PonyConfig(Path path) { public PonyConfig(Path path) {
super(HEIRARCHICAL_JSON_ADAPTER, path); super(HEIRARCHICAL_JSON_ADAPTER, path);
instance = this; instance = this;
@ -129,4 +143,18 @@ public class PonyConfig extends Config {
return race; return race;
} }
public static Size getEffectiveSize(Size size) {
SizePreset sz = instance.sizeOverride.get();
if (sz != SizePreset.UNSET) {
return sz;
}
if (size == SizePreset.UNSET || !instance.sizes.get()) {
return SizePreset.NORMAL;
}
return size;
}
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,9 +1,7 @@
package com.minelittlepony.api.model; package com.minelittlepony.api.model;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.pony.*; import com.minelittlepony.api.pony.*;
import com.minelittlepony.client.IPreviewModel;
import com.minelittlepony.client.SkinsProxy;
import com.minelittlepony.client.pony.PonyData;
import com.minelittlepony.common.util.animation.Interpolator; import com.minelittlepony.common.util.animation.Interpolator;
import com.minelittlepony.util.MathUtil; import com.minelittlepony.util.MathUtil;
@ -11,8 +9,9 @@ import java.util.*;
import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose; import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier; import net.minecraft.item.ItemStack;
import net.minecraft.util.*;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -21,6 +20,11 @@ public class ModelAttributes {
* True if the model is sleeping in a bed. * True if the model is sleeping in a bed.
*/ */
public boolean isSleeping; public boolean isSleeping;
/**
* True if the model is lying down comfortably
*/
public boolean isLyingDown;
/** /**
* True if the model is flying like a pegasus. * True if the model is flying like a pegasus.
*/ */
@ -64,6 +68,10 @@ public class ModelAttributes {
* Flag indicating that this model is performing a rainboom (flight). * Flag indicating that this model is performing a rainboom (flight).
*/ */
public boolean isGoingFast; public boolean isGoingFast;
/**
* Flag indicating that this model should mimic the vanilla horse models.
*/
public boolean isHorsey;
/** /**
* Vertical pitch whilst flying. * Vertical pitch whilst flying.
@ -105,16 +113,21 @@ public class ModelAttributes {
/** /**
* Contains the skin metadata associated with this model. * Contains the skin metadata associated with this model.
*/ */
public IPonyData metadata = PonyData.NULL; public PonyData metadata = PonyData.NULL;
public Arm mainArm;
public Hand activeHand;
public ItemStack heldStack = ItemStack.EMPTY;
public int itemUseTime;
/** /**
* Checks flying and speed conditions and sets rainboom to true if we're a species with wings and is going faaast. * Checks flying and speed conditions and sets rainboom to true if we're a species with wings and is going faaast.
*/ */
public void checkRainboom(LivingEntity entity, boolean hasWings, float ticks) { public void checkRainboom(LivingEntity entity, PonyModel<?> model, float ticks) {
Vec3d motion = entity.getVelocity(); Vec3d motion = entity.getVelocity();
double zMotion = Math.sqrt(motion.x * motion.x + motion.z * motion.z); double zMotion = Math.sqrt(motion.x * motion.x + motion.z * motion.z);
isGoingFast = (isFlying && hasWings) || isGliding; isGoingFast = (isFlying && model instanceof WingedPonyModel) || isGliding;
isGoingFast &= zMotion > 0.4F; isGoingFast &= zMotion > 0.4F;
isGoingFast |= entity.isUsingRiptide(); isGoingFast |= entity.isUsingRiptide();
isGoingFast |= entity.isFallFlying(); isGoingFast |= entity.isFallFlying();
@ -129,31 +142,41 @@ public class ModelAttributes {
return (MathHelper.sin(ticks * 0.136f) / 2) + MathUtil.Angles._270_DEG; return (MathHelper.sin(ticks * 0.136f) / 2) + MathUtil.Angles._270_DEG;
} }
if (isFlying) { if (isFlying) {
return MathHelper.sin(ticks * 0.536f) + IPegasus.WINGS_FULL_SPREAD_ANGLE; return MathHelper.sin(ticks * 0.536f) + WingedPonyModel.WINGS_FULL_SPREAD_ANGLE;
} }
return IPegasus.WINGS_RAISED_ANGLE; return WingedPonyModel.WINGS_RAISED_ANGLE;
} }
public void updateLivingState(LivingEntity entity, IPony pony, Mode mode) { public void updateLivingState(LivingEntity entity, Pony pony, Mode mode) {
visualHeight = entity.getHeight() + 0.125F; visualHeight = entity.getHeight() + 0.125F;
isSitting = PonyPosture.isSitting(entity); isSitting = PonyPosture.isSitting(entity);
isCrouching = !isSitting && mode == Mode.THIRD_PERSON && PonyPosture.isCrouching(pony, entity); isSleeping = entity.isAlive() && entity.isSleeping();;
isSleeping = entity.isAlive() && entity.isSleeping(); isLyingDown = isSleeping;
isFlying = mode == Mode.THIRD_PERSON && PonyPosture.isFlying(entity); if (entity instanceof PlayerEntity) {
boolean moving = entity.getVelocity().multiply(1, 0, 1).length() == 0 && entity.isSneaking();
isLyingDown |= getMainInterpolator().interpolate("lyingDown", moving ? 10 : 0, 200) >= 9;
}
isCrouching = !isLyingDown && !isSitting && mode == Mode.THIRD_PERSON && PonyPosture.isCrouching(pony, entity);
isFlying = !isLyingDown && mode == Mode.THIRD_PERSON && PonyPosture.isFlying(entity);
isGliding = entity.isFallFlying(); isGliding = entity.isFallFlying();
isSwimming = mode == Mode.THIRD_PERSON && PonyPosture.isSwimming(entity); isSwimming = mode == Mode.THIRD_PERSON && PonyPosture.isSwimming(entity);
isSwimmingRotated = isSwimming; isSwimmingRotated = isSwimming;
isRiptide = entity.isUsingRiptide(); isRiptide = entity.isUsingRiptide();
isRidingInteractive = PonyPosture.isRidingAPony(entity); isRidingInteractive = PonyPosture.isRidingAPony(entity);
if (!(entity instanceof IPreviewModel)) { if (!(entity instanceof PreviewModel)) {
interpolatorId = entity.getUuid(); interpolatorId = entity.getUuid();
} }
isLeftHanded = entity.getMainArm() == Arm.LEFT; isLeftHanded = entity.getMainArm() == Arm.LEFT;
isHorsey = PonyConfig.getInstance().horsieMode.get();
featureSkins = SkinsProxy.instance.getAvailableSkins(entity); featureSkins = SkinsProxy.instance.getAvailableSkins(entity);
mainArm = entity.getMainArm();
activeHand = entity.getActiveHand();
itemUseTime = entity.getItemUseTimeLeft();
} }
public Interpolator getMainInterpolator() { public Interpolator getMainInterpolator() {
return metadata.getInterpolator(interpolatorId); return Interpolator.linear(interpolatorId);
} }
public boolean shouldLiftArm(ArmPose pose, ArmPose complement, float sigma) { public boolean shouldLiftArm(ArmPose pose, ArmPose complement, float sigma) {

View file

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

View file

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

View file

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

View file

@ -0,0 +1,7 @@
package com.minelittlepony.api.model;
public interface PreviewModel {
boolean forceSeapony();
boolean forceNirik();
}

View file

@ -3,20 +3,18 @@ package com.minelittlepony.api.model;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
public interface IPart { public interface SubModel {
/** /**
* Sets the model's various rotation angles. * Sets the model's various rotation angles.
* <p>
* See {@link AbstractPonyMode.setRotationAndAngle} for an explanation of the various parameters.
*/ */
default void setRotationAndAngles(ModelAttributes attributes, float move, float swing, float bodySwing, float ticks) { default void setPartAngles(ModelAttributes attributes, float limbAngle, float limbSpeed, float bodySwing, float animationProgress) {
} }
/** /**
* Renders this model component. * Renders this model component.
*/ */
void renderPart(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha, ModelAttributes attributes); void renderPart(MatrixStack stack, VertexConsumer vertices, int overlay, int light, float red, float green, float blue, float alpha, ModelAttributes attributes);
/** /**
* Sets whether this part should be rendered. * Sets whether this part should be rendered.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,18 +1,19 @@
package com.minelittlepony.client.model.gear; package com.minelittlepony.api.model.gear;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import com.minelittlepony.api.model.BodyPart; import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.model.IModel; import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
public abstract class AbstractWearableGear extends AbstractGear { public class WearableGear extends AbstractGearModel {
protected final Wearable wearable; protected final Wearable wearable;
protected final BodyPart location; protected final BodyPart location;
protected AbstractWearableGear(Wearable wearable, BodyPart location) { public WearableGear(Wearable wearable, BodyPart location, float stackingHeight) {
super(stackingHeight);
this.wearable = wearable; this.wearable = wearable;
this.location = location; this.location = location;
} }
@ -23,7 +24,7 @@ public abstract class AbstractWearableGear extends AbstractGear {
} }
@Override @Override
public boolean canRender(IModel model, Entity entity) { public boolean canRender(PonyModel<?> model, Entity entity) {
return model.isWearing(wearable); return model.isWearing(wearable);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,32 @@
package com.minelittlepony.api.pony;
import com.mojang.authlib.GameProfile;
import java.util.Optional;
import java.util.Set;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.texture.PlayerSkinProvider;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
/**
* Proxy handler for getting player skin data from HDSkins
*/
public class SkinsProxy {
public static SkinsProxy instance = new SkinsProxy();
public Identifier getSkinTexture(GameProfile profile) {
PlayerSkinProvider skins = MinecraftClient.getInstance().getSkinProvider();
return skins.getSkinTextures(profile).texture();
}
public Optional<Identifier> getSkin(Identifier skinTypeId, AbstractClientPlayerEntity player) {
return Optional.empty();
}
public Set<Identifier> getAvailableSkins(Entity entity) {
return Set.of();
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
package com.minelittlepony.client.pony; package com.minelittlepony.client;
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@ -6,13 +6,14 @@ import net.minecraft.entity.Entity;
import net.minecraft.resource.ResourceManager; import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.util.MathUtil; import com.minelittlepony.util.MathUtil;
import java.util.*; import java.util.*;
public class VariatedTextureSupplier implements SimpleSynchronousResourceReloadListener { public class VariatedTextureSupplier implements SimpleSynchronousResourceReloadListener {
private static final Identifier ID = new Identifier("minelittlepony", "variated_textures"); private static final Identifier ID = new Identifier("minelittlepony", "variated_textures");
public static final Identifier BACKGROUND_PONIES_POOL = new Identifier("minelittlepony", "textures/entity/pony");
public static final Identifier BACKGROUND_ZOMPONIES_POOL = new Identifier("minelittlepony", "textures/entity/zompony");
private final Map<Identifier, SkinList> entries = new HashMap<>(); private final Map<Identifier, SkinList> entries = new HashMap<>();

View file

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

View file

@ -0,0 +1,71 @@
package com.minelittlepony.client.compat.hdskins;
import com.minelittlepony.client.PonySettingsScreen;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.common.client.gui.dimension.Bounds;
import com.minelittlepony.common.client.gui.element.Button;
import com.minelittlepony.common.client.gui.sprite.TextureSprite;
import com.minelittlepony.hdskins.client.gui.DualCarouselWidget;
import com.minelittlepony.hdskins.client.gui.GuiSkins;
import com.minelittlepony.hdskins.server.SkinServerList;
import com.minelittlepony.hdskins.profile.SkinType;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.util.Identifier;
/**
* Skin uploading GUI. Usually displayed over the main menu.
*/
class GuiSkinsMineLP extends GuiSkins {
private static final String[] PANORAMAS = new String[] {
"minelittlepony:textures/cubemap/sugarcubecorner",
"minelittlepony:textures/cubemap/quillsandsofas",
"minelittlepony:textures/cubemap/sweetappleacres"
};
public GuiSkinsMineLP(Screen parent, SkinServerList servers) {
super(parent, servers);
chooser.addSkinChangedEventListener(type -> {
MineLittlePony.logger.debug("Invalidating old local skin, checking updated local skin");
if (type == SkinType.SKIN) {
MineLittlePony.getInstance().getManager().removePony(previewer.getLocal().getSkins().get(SkinType.SKIN).getId());
}
});
uploader.addSkinLoadedEventListener((type, location, profileTexture) -> {
MineLittlePony.logger.debug("Invalidating old remote skin, checking updated remote skin");
if (type == SkinType.SKIN) {
MineLittlePony.getInstance().getManager().removePony(location);
}
});
}
@Override
protected void initServerPreviewButtons(Bounds area) {
if (!(parent instanceof PonySettingsScreen)) {
addButton(new Button(area.right() - 20, area.bottom() + 5, 20, 20))
.onClick(sender -> client.setScreen(new PonySettingsScreen(this)))
.getStyle()
.setIcon(new TextureSprite()
.setPosition(2, 2)
.setTexture(new Identifier("minelittlepony", "textures/gui/pony.png"))
.setTextureSize(16, 16)
.setSize(16, 16))
.setTooltip("minelp.options.title", 0, 10);
super.initServerPreviewButtons(new Bounds(area.top, area.left, area.width - 25, area.height));
} else {
super.initServerPreviewButtons(area);
}
}
@Override
public DualCarouselWidget createPreviewer() {
return new PonifiedDualCarouselWidget(this);
}
@Override
protected Identifier getBackground() {
int i = (int)Math.floor(Math.random() * PANORAMAS.length);
return new Identifier(PANORAMAS[i]);
}
}

View file

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

View file

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

View file

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

View file

@ -1,13 +1,13 @@
package com.minelittlepony.client.modmenu; package com.minelittlepony.client.compat.modmenu;
import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi; import com.terraformersmc.modmenu.api.ModMenuApi;
import com.minelittlepony.client.GuiPonySettings; import com.minelittlepony.client.PonySettingsScreen;
public class MineLPModMenuFactory implements ModMenuApi { public class MineLPModMenuFactory implements ModMenuApi {
@Override @Override
public ConfigScreenFactory<?> getModConfigScreenFactory() { public ConfigScreenFactory<?> getModConfigScreenFactory() {
return GuiPonySettings::new; return PonySettingsScreen::new;
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import com.minelittlepony.client.render.PonyRenderDispatcher; import com.minelittlepony.client.MineLittlePony;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -42,6 +42,6 @@ abstract class MixinHeldItemRenderer {
VertexConsumerProvider renderContext, VertexConsumerProvider renderContext,
@Nullable World world, @Nullable World world,
int lightUv, int overlayUv, int posLong) { int lightUv, int overlayUv, int posLong) {
PonyRenderDispatcher.getInstance().getMagicRenderer().renderItemInFirstPerson(target, entity, item, transform, left, stack, renderContext, world, lightUv, posLong); MineLittlePony.getInstance().getRenderDispatcher().getMagicRenderer().renderItem(target, entity, item, transform, left, stack, renderContext, world, lightUv, posLong);
} }
} }

View file

@ -1,11 +1,12 @@
package com.minelittlepony.client.model; package com.minelittlepony.client.model;
import com.minelittlepony.api.model.*; import com.minelittlepony.api.model.*;
import com.minelittlepony.api.model.fabric.PonyModelPrepareCallback; import com.minelittlepony.api.events.PonyModelPrepareCallback;
import com.minelittlepony.api.pony.meta.Sizes; import com.minelittlepony.api.pony.meta.SizePreset;
import com.minelittlepony.client.transform.PonyTransformation; import com.minelittlepony.client.transform.PonyTransformation;
import com.minelittlepony.client.util.render.RenderList; import com.minelittlepony.client.util.render.RenderList;
import com.minelittlepony.util.MathUtil; import com.minelittlepony.util.MathUtil;
import com.minelittlepony.util.MathUtil.Angles;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -15,7 +16,7 @@ import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm; import net.minecraft.util.*;
import net.minecraft.util.math.*; import net.minecraft.util.math.*;
/** /**
@ -49,7 +50,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
protected final RenderList mainRenderList; protected final RenderList mainRenderList;
private final List<IPart> parts = new ArrayList<>(); private final List<SubModel> parts = new ArrayList<>();
public AbstractPonyModel(ModelPart tree) { public AbstractPonyModel(ModelPart tree) {
super(tree); super(tree);
@ -65,18 +66,18 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
.add(withStage(BodyPart.HEAD, helmetRenderList = RenderList.of(hat))); .add(withStage(BodyPart.HEAD, helmetRenderList = RenderList.of(hat)));
} }
protected <P extends IPart> P addPart(P part) { protected <P extends SubModel> P addPart(P part) {
parts.add(part); parts.add(part);
return part; return part;
} }
protected RenderList forPart(Supplier<IPart> part) { protected RenderList forPart(Supplier<SubModel> part) {
return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> { return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
part.get().renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes); part.get().renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes);
}; };
} }
protected RenderList forPart(IPart part) { protected RenderList forPart(SubModel part) {
return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> { return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
part.renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes); part.renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes);
}; };
@ -101,11 +102,11 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
*/ */
@Override @Override
public final void setAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) { public final void setAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) {
attributes.checkRainboom(entity, canFly(), animationProgress); attributes.checkRainboom(entity, this, animationProgress);
PonyModelPrepareCallback.EVENT.invoker().onPonyModelPrepared(entity, this, ModelAttributes.Mode.OTHER); PonyModelPrepareCallback.EVENT.invoker().onPonyModelPrepared(entity, this, ModelAttributes.Mode.OTHER);
super.setAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch); super.setAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch);
head.setPivot(head.getDefaultTransform().pivotX, head.getDefaultTransform().pivotY, head.getDefaultTransform().pivotZ); resetPivot(head, neck, leftArm, rightArm, leftLeg, rightLeg);
setModelAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch); setModelAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch);
@ -118,11 +119,10 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
} }
protected void setModelAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) { protected void setModelAngles(T entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) {
float pitch = attributes.motionPitch * MathHelper.RADIANS_PER_DEGREE;
float pitch = (float)Math.toRadians(attributes.motionPitch);
head.setAngles( head.setAngles(
MathHelper.clamp(attributes.isSleeping ? 0.1f : headPitch / 57.29578F, -1.25f - pitch, 0.5f - pitch), MathHelper.clamp(attributes.isSleeping ? 0.1f : headPitch / 57.29578F, -1.25f - pitch, 0.5f - pitch),
attributes.isSleeping ? (Math.abs(entity.getUuid().getMostSignificantBits()) % 2.8F) - 1.9F : headYaw / 57.29578F, attributes.isSleeping ? (Math.signum(MathHelper.wrapDegrees(headYaw)) * 1.3F) : headYaw * MathHelper.RADIANS_PER_DEGREE,
0 0
); );
@ -148,10 +148,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
} else { } else {
adjustBody(0, ORIGIN); adjustBody(0, ORIGIN);
rightLeg.pivotY = FRONT_LEGS_Y; if (!attributes.isLyingDown) {
leftLeg.pivotY = FRONT_LEGS_Y;
if (!attributes.isSleeping) {
animateBreathing(animationProgress); animateBreathing(animationProgress);
} }
@ -161,11 +158,17 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
} }
} }
if (attributes.isSleeping) { if (attributes.isLyingDown) {
ponySleep(); ponySleep();
} }
parts.forEach(part -> part.setRotationAndAngles(attributes, limbAngle, limbSpeed, wobbleAmount, animationProgress)); if (attributes.isHorsey) {
head.pivotY -= 3;
head.pivotZ -= 2;
head.pitch = 0.5F;
}
parts.forEach(part -> part.setPartAngles(attributes, limbAngle, limbSpeed, wobbleAmount, animationProgress));
} }
public void setHeadRotation(float animationProgress, float yaw, float pitch) { public void setHeadRotation(float animationProgress, float yaw, float pitch) {
@ -183,9 +186,6 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
rightArm.pitch -= LEG_SNEAKING_PITCH_ADJUSTMENT; rightArm.pitch -= LEG_SNEAKING_PITCH_ADJUSTMENT;
leftArm.pitch -= LEG_SNEAKING_PITCH_ADJUSTMENT; leftArm.pitch -= LEG_SNEAKING_PITCH_ADJUSTMENT;
leftLeg.pivotY = FRONT_LEGS_Y;
rightLeg.pivotY = FRONT_LEGS_Y;
} }
protected void ponySleep() { protected void ponySleep() {
@ -196,7 +196,6 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
leftLeg.pitch = MathUtil.Angles._90_DEG; leftLeg.pitch = MathUtil.Angles._90_DEG;
HEAD_SLEEPING.set(head); HEAD_SLEEPING.set(head);
head.pivotZ = sneaking ? -1 : 1;
FONT_LEGS_SLEEPING.add(rightArm); FONT_LEGS_SLEEPING.add(rightArm);
FONT_LEGS_SLEEPING.add(leftArm); FONT_LEGS_SLEEPING.add(leftArm);
@ -275,6 +274,9 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
leftArm.pivotZ = 2 - sin; leftArm.pivotZ = 2 - sin;
float legRPX = attributes.getMainInterpolator().interpolate("legOffset", cos - getLegOutset() - 0.001F, 2); float legRPX = attributes.getMainInterpolator().interpolate("legOffset", cos - getLegOutset() - 0.001F, 2);
if (attributes.isHorsey) {
legRPX += 2;
}
rightArm.pivotX = -legRPX; rightArm.pivotX = -legRPX;
rightLeg.pivotX = -legRPX; rightLeg.pivotX = -legRPX;
@ -285,8 +287,12 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
rightArm.yaw += body.yaw; rightArm.yaw += body.yaw;
leftArm.yaw += body.yaw; leftArm.yaw += body.yaw;
rightArm.pivotY = leftArm.pivotY = 8; if (attributes.isHorsey) {
rightLeg.pivotZ = leftLeg.pivotZ = 11; rightArm.pivotZ = leftArm.pivotZ = -1;
rightArm.pivotY = leftArm.pivotY = 6;
rightLeg.pivotZ = leftLeg.pivotZ = 19;
rightLeg.pivotY = leftLeg.pivotY = 6;
}
} }
/** /**
@ -333,7 +339,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
} }
protected float getLegOutset() { protected float getLegOutset() {
if (attributes.isSleeping) { if (attributes.isLyingDown) {
return 3.6f; return 3.6f;
} }
@ -360,6 +366,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
case NECK: return neck; case NECK: return neck;
case TAIL: case TAIL:
case LEGS: case LEGS:
case BACK:
case BODY: return body; case BODY: return body;
} }
} }
@ -380,13 +387,14 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
if (attributes.shouldLiftArm(pose, complement, sigma)) { if (attributes.shouldLiftArm(pose, complement, sigma)) {
float swag = 1; float swag = 1;
if (!isFlying() && both) { if (!getAttributes().isFlying && both) {
swag -= (float)Math.pow(limbSpeed, 2); swag -= (float)Math.pow(limbSpeed, 2);
} }
float mult = 1 - swag/2; float mult = 1 - swag/2;
arm.pitch = arm.pitch * mult - (MathHelper.PI / 10) * swag; arm.pitch = arm.pitch * mult - (MathHelper.PI / 10) * swag;
arm.roll = -sigma * (MathHelper.PI / 15); arm.roll = -sigma * (MathHelper.PI / 15);
arm.roll += 0.3F * -limbSpeed * sigma;
if (attributes.isCrouching) { if (attributes.isCrouching) {
arm.pivotX -= sigma * 2; arm.pivotX -= sigma * 2;
@ -400,6 +408,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
case BLOCK: case BLOCK:
arm.pitch = (arm.pitch / 2 - 0.9424779F) - 0.3F; arm.pitch = (arm.pitch / 2 - 0.9424779F) - 0.3F;
arm.yaw = sigma * MathHelper.PI / 9; arm.yaw = sigma * MathHelper.PI / 9;
arm.roll += 0.3F * -limbSpeed * sigma;
if (complement == pose) { if (complement == pose) {
arm.yaw -= sigma * MathHelper.PI / 18; arm.yaw -= sigma * MathHelper.PI / 18;
} }
@ -423,9 +432,12 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
arm.pitch = -0.8F; arm.pitch = -0.8F;
arm.yaw = head.yaw + 0.06F; arm.yaw = head.yaw + 0.06F;
arm.roll += 0.3F * -limbSpeed * sigma;
break; break;
case THROW_SPEAR: case THROW_SPEAR:
arm.pitch = MathUtil.Angles._90_DEG * 2; arm.pitch = MathUtil.Angles._90_DEG * 2;
arm.roll += (0.3F * -limbSpeed + 0.6F) * sigma;
arm.pivotY ++;
break; break;
case SPYGLASS: case SPYGLASS:
float addedPitch = sneaking ? -0.2617994F : 0; float addedPitch = sneaking ? -0.2617994F : 0;
@ -438,13 +450,24 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
arm.pivotX -= 6 * sigma; arm.pivotX -= 6 * sigma;
arm.pivotZ -= 2; arm.pivotZ -= 2;
} }
if (getSize() == Sizes.TALL) { if (getSize() == SizePreset.TALL) {
arm.pivotY += 1; arm.pivotY += 1;
} }
if (getSize() == Sizes.FOAL) { if (getSize() == SizePreset.FOAL) {
arm.pivotY -= 2; arm.pivotY -= 2;
} }
break;
case TOOT_HORN:
arm.pitch = MathHelper.clamp(head.pitch, -0.55f, 1.2f) - 1.7835298f;
arm.yaw = head.yaw - 0.1235988f * sigma;
arm.pivotY += 3;
arm.roll += 0.3F * -limbSpeed * sigma;
break;
case BRUSH:
arm.pitch = arm.pitch * 0.5f - 0.62831855f;
arm.yaw = 0;
arm.roll += 0.3F * -limbSpeed * sigma;
break; break;
default: default:
break; break;
@ -467,7 +490,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
* @param entity The entity we are being called for. * @param entity The entity we are being called for.
*/ */
protected void swingItem(T entity) { protected void swingItem(T entity) {
if (getSwingAmount() > 0 && !attributes.isSleeping) { if (getSwingAmount() > 0 && !attributes.isLyingDown) {
Arm mainSide = getPreferredArm(entity); Arm mainSide = getPreferredArm(entity);
swingArm(getArm(mainSide)); swingArm(getArm(mainSide));
@ -517,7 +540,14 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
protected void adjustBody(float pitch, Pivot pivot) { protected void adjustBody(float pitch, Pivot pivot) {
adjustBodyComponents(pitch, pivot); adjustBodyComponents(pitch, pivot);
if (!attributes.isHorsey) {
neck.setPivot(NECK_X + pitch, pivot.y(), pivot.z()); neck.setPivot(NECK_X + pitch, pivot.y(), pivot.z());
rightLeg.pivotY = FRONT_LEGS_Y;
leftLeg.pivotY = FRONT_LEGS_Y;
} else {
neck.setPivot(NECK_X + pitch, pivot.y() - 1, pivot.z() - 2);
neck.pitch = Angles._30_DEG;
}
} }
protected void adjustBodyComponents(float pitch, Pivot pivot) { protected void adjustBodyComponents(float pitch, Pivot pivot) {
@ -528,7 +558,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
@Override @Override
public float getRiderYOffset() { public float getRiderYOffset() {
switch ((Sizes)getSize()) { switch ((SizePreset)getSize()) {
case NORMAL: return 0.4F; case NORMAL: return 0.4F;
case FOAL: case FOAL:
case TALL: case TALL:
@ -541,16 +571,77 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
super.setVisible(visible); super.setVisible(visible);
neck.visible = visible; neck.visible = visible;
hat.visible &= !attributes.isHorsey;
parts.forEach(part -> part.setVisible(visible, attributes)); parts.forEach(part -> part.setVisible(visible, attributes));
} }
@Override
public final void setArmAngle(Arm arm, MatrixStack matrices) {
super.setArmAngle(arm, matrices);
positionheldItem(arm, matrices);
}
protected void positionheldItem(Arm arm, MatrixStack matrices) {
float left = arm == Arm.LEFT ? -1 : 1;
UseAction action = getAttributes().heldStack.getUseAction();
if (action == UseAction.SPYGLASS && getAttributes().itemUseTime > 0) {
Arm main = getAttributes().mainArm;
if (getAttributes().activeHand == Hand.OFF_HAND) {
main = main.getOpposite();
}
if (main == arm) {
matrices.translate(left * -0.05F, 0.5F, 0.2F);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-60));
return;
}
}
if (getAttributes().isRidingInteractive) {
matrices.translate(left / 10, -0.2F, -0.5F);
}
matrices.translate(-left * 0.1F, 0.45F, 0);
if (getAttributes().heldStack.getUseAction() == UseAction.BLOCK && getAttributes().itemUseTime == 0) {
matrices.translate(left * 0.02F, -0.25F, 0);
}
}
@Override @Override
public void transform(BodyPart part, MatrixStack stack) { public void transform(BodyPart part, MatrixStack stack) {
if (attributes.isHorsey) {
stack.translate(0, 0.1F, 0);
}
if (attributes.isSleeping || attributes.isRiptide) { if (attributes.isSleeping || attributes.isRiptide) {
stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180)); stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180));
} }
boolean crouching = attributes.isCrouching;
if (attributes.isLyingDown && !attributes.isSleeping) {
stack.translate(0, 1.35F, 0);
attributes.isCrouching = sneaking;
}
if (attributes.isHorsey) {
if (part == BodyPart.BODY) {
stack.scale(1.5F, 1, 1.5F);
}
neck.visible = head.visible;
} else {
neck.hidden = !head.visible;
}
PonyTransformation.forSize(getSize()).transform(this, part, stack); PonyTransformation.forSize(getSize()).transform(this, part, stack);
attributes.isCrouching = crouching;
} }
} }

View file

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

View file

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

View file

@ -0,0 +1,106 @@
package com.minelittlepony.client.model;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.ModelPart;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
import com.google.common.base.Preconditions;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.mson.api.*;
import com.minelittlepony.mson.api.MsonModel.Factory;
import com.minelittlepony.mson.api.model.traversal.PartSkeleton;
import com.minelittlepony.mson.api.model.traversal.SkeletonisedModel;
import com.minelittlepony.mson.api.parser.FileContent;
import com.minelittlepony.mson.api.parser.locals.LocalBlock;
import com.minelittlepony.mson.impl.model.RootContext;
import java.util.*;
import java.util.concurrent.CompletableFuture;
final class ModelKeyImpl<M extends Model> implements ModelKey<M>, LocalBlock {
private final Map<String, Incomplete<Float>> horseModeValues = Util.make(new HashMap<>(), map -> {
map.put("head_elongation", Incomplete.completed(-1F));
map.put("neck_dilate_z", Incomplete.completed(1.5F));
map.put("neck_dilate_y", Incomplete.completed(3F));
map.put("global_ear_shortening", Incomplete.completed(-0.5F));
});
private final ModelKey<M> key;
private final MsonModel.Factory<M> constr;
ModelKeyImpl(Identifier id, MsonModel.Factory<M> constr) {
this.key = Mson.getInstance().registerModel(id, constr);
this.constr = constr;
}
@Override
public Identifier getId() {
return key.getId();
}
@SuppressWarnings("unchecked")
@Override
public <V extends M> V createModel() {
return (V)createModel(constr);
}
@Override
public <V extends M> V createModel(Factory<V> factory) {
Preconditions.checkNotNull(factory, "Factory should not be null");
return getModelData().map(content -> {
ModelContext ctx = getModelContext(content);
ModelPart root = ctx.toTree();
V t = factory.create(root);
if (t instanceof SkeletonisedModel) {
((SkeletonisedModel)t).setSkeleton(content.getSkeleton()
.map(s -> PartSkeleton.of(root, s))
.orElseGet(() -> PartSkeleton.of(root)));
}
if (t instanceof MsonModel) {
if (ctx instanceof RootContext) {
((RootContext)ctx).setModel(t);
}
((MsonModel)t).init(ctx);
}
return t;
})
.orElseThrow(() -> new IllegalStateException("Model file for " + getId() + " was not loaded!"));
}
@Override
public Optional<ModelPart> createTree() {
return getModelData().map(this::getModelContext).map(ModelContext::toTree);
}
private ModelContext getModelContext(FileContent<?> content) {
if (PonyConfig.getInstance().horsieMode.get()) {
return content.createContext(null, null, content.getLocals().extendWith(getId(), Optional.of(this), Optional.empty()).bake());
}
return content.createContext(null, null, content.getLocals().bake());
}
@Override
public Optional<FileContent<?>> getModelData() {
return key.getModelData();
}
@Override
public Set<String> appendKeys(Set<String> output) {
output.addAll(horseModeValues.keySet());
return output;
}
@Override
public Optional<CompletableFuture<Incomplete<Float>>> get(String name) {
if (horseModeValues.containsKey(name)) {
return Optional.of(CompletableFuture.completedFuture(horseModeValues.get(name)));
}
return Optional.empty();
}
}

View file

@ -8,16 +8,15 @@ import net.minecraft.entity.mob.VexEntity;
import net.minecraft.entity.passive.*; import net.minecraft.entity.passive.*;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import com.minelittlepony.api.model.IModel; import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.model.gear.IGear; 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.Race;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.model.armour.PonyArmourModel; import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.client.model.entity.*; import com.minelittlepony.client.model.entity.*;
import com.minelittlepony.client.model.entity.race.*; import com.minelittlepony.client.model.entity.race.*;
import com.minelittlepony.client.model.gear.*; import com.minelittlepony.client.model.gear.*;
import com.minelittlepony.client.render.entity.PlayerPonyRenderer;
import com.minelittlepony.client.render.entity.PlayerSeaponyRenderer;
import com.minelittlepony.mson.api.ModelKey; import com.minelittlepony.mson.api.ModelKey;
import com.minelittlepony.mson.api.Mson; import com.minelittlepony.mson.api.Mson;
import com.minelittlepony.mson.api.MsonModel; import com.minelittlepony.mson.api.MsonModel;
@ -31,7 +30,7 @@ import java.util.stream.Stream;
public final class ModelType { public final class ModelType {
private static final Map<Race, PlayerModelKey<?, ?>> PLAYER_MODELS = new HashMap<>(); private static final Map<Race, PlayerModelKey<?, ?>> PLAYER_MODELS = new HashMap<>();
private static final Map<Wearable, GearModelKey<? extends IGear>> GEAR_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); public static final ModelKey<DJPon3EarsModel> DJ_PON_3 = register("dj_pon_three", DJPon3EarsModel::new);
@ -57,78 +56,69 @@ public final class ModelType {
public static final ModelKey<PonyArmourModel<?>> INNER_PONY_ARMOR = register("armor/inner_pony_armor", PonyArmourModel::new); public static final ModelKey<PonyArmourModel<?>> INNER_PONY_ARMOR = register("armor/inner_pony_armor", PonyArmourModel::new);
public static final ModelKey<PonyArmourModel<?>> OUTER_PONY_ARMOR = register("armor/outer_pony_armor", PonyArmourModel::new); public static final ModelKey<PonyArmourModel<?>> OUTER_PONY_ARMOR = register("armor/outer_pony_armor", PonyArmourModel::new);
public static final GearModelKey<Stetson> STETSON = registerGear("stetson", Wearable.STETSON, Stetson::new); public static final GearModelKey<AbstractGearModel> STETSON = registerGear("stetson", Wearable.STETSON, t -> new WearableGear(Wearable.STETSON, BodyPart.HEAD, 0.15F));
public static final GearModelKey<SaddleBags> SADDLEBAGS_BOTH = registerGear("saddlebags", Wearable.SADDLE_BAGS_BOTH, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_BOTH)); public static final GearModelKey<SaddleBags> SADDLEBAGS_BOTH = registerGear("saddlebags", Wearable.SADDLE_BAGS_BOTH, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_BOTH));
public static final GearModelKey<SaddleBags> SADDLEBAGS_LEFT = registerGear(SADDLEBAGS_BOTH, Wearable.SADDLE_BAGS_LEFT, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_LEFT)); public static final GearModelKey<SaddleBags> SADDLEBAGS_LEFT = registerGear(SADDLEBAGS_BOTH, Wearable.SADDLE_BAGS_LEFT, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_LEFT));
public static final GearModelKey<SaddleBags> SADDLEBAGS_RIGHT = registerGear(SADDLEBAGS_BOTH, Wearable.SADDLE_BAGS_RIGHT, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_RIGHT)); public static final GearModelKey<SaddleBags> SADDLEBAGS_RIGHT = registerGear(SADDLEBAGS_BOTH, Wearable.SADDLE_BAGS_RIGHT, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_RIGHT));
public static final GearModelKey<Crown> CROWN = registerGear("crown", Wearable.CROWN, Crown::new); public static final GearModelKey<Crown> CROWN = registerGear("crown", Wearable.CROWN, Crown::new);
public static final GearModelKey<Muffin> MUFFIN = registerGear("muffin", Wearable.MUFFIN, Muffin::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<WitchHat> WITCH_HAT = registerGear("witch_hat", Wearable.HAT, WitchHat::new); public static final GearModelKey<AbstractGearModel> WITCH_HAT = registerGear("witch_hat", Wearable.HAT, t -> new WearableGear(Wearable.HAT, BodyPart.HEAD, 0.7F).addPart(t.getChild("hat")));
public static final GearModelKey<ChristmasHat> ANTLERS = registerGear("antlers", Wearable.ANTLERS, ChristmasHat::new); 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, AlicornModel<?>> ALICORN = registerPlayer("alicorn", Race.ALICORN, AlicornModel::new);
public static final PlayerModelKey<LivingEntity, UnicornModel<?>> UNICORN = registerPlayer("unicorn", Race.UNICORN, UnicornModel::new); public static final PlayerModelKey<LivingEntity, UnicornModel<?>> UNICORN = registerPlayer("unicorn", Race.UNICORN, UnicornModel::new);
public static final PlayerModelKey<LivingEntity, UnicornModel<?>> KIRIN = registerPlayer("kirin", Race.KIRIN, 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<?>> PEGASUS = registerPlayer("pegasus", Race.PEGASUS, PegasusModel::new);
public static final PlayerModelKey<LivingEntity, PegasusModel<?>> GRYPHON = registerPlayer("gryphon", Race.GRYPHON, PegasusModel::new); public static final PlayerModelKey<LivingEntity, PegasusModel<?>> GRYPHON = registerPlayer("gryphon", Race.GRYPHON, PegasusModel::new);
public static final PlayerModelKey<LivingEntity, PegasusModel<?>> HIPPOGRIFF = registerPlayer("hippogriff", Race.HIPPOGRIFF, PegasusModel::new); 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, 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, PlayerSeaponyRenderer::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, PegasusModel<?>> BAT_PONY = registerPlayer("bat_pony", Race.BATPONY, PegasusModel::new);
public static final PlayerModelKey<LivingEntity, ChangelingModel<?>> CHANGELING = registerPlayer("changeling", Race.CHANGELING, ChangelingModel::new); public static final PlayerModelKey<LivingEntity, ChangelingModel<?>> CHANGELING = registerPlayer("changeling", Race.CHANGELING, ChangelingModel::new);
public static final PlayerModelKey<LivingEntity, ChangelingModel<?>> CHANGEDLING = registerPlayer("reformed_changeling", Race.CHANGEDLING, ChangelingModel::new); public static final PlayerModelKey<LivingEntity, ChangelingModel<?>> CHANGEDLING = registerPlayer("reformed_changeling", Race.CHANGEDLING, ChangelingModel::new);
public static final PlayerModelKey<LivingEntity, EarthPonyModel<?>> ZEBRA = registerPlayer("zebra", Race.ZEBRA, EarthPonyModel::new); public static final PlayerModelKey<LivingEntity, EarthPonyModel<?>> ZEBRA = registerPlayer("zebra", Race.ZEBRA, EarthPonyModel::new);
static <E extends LivingEntity, T extends Model & MsonModel & IModel> PlayerModelKey<E, T> registerPlayer(String name, Race race, static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<E, T> registerPlayer(String name, Race race,
BiFunction<ModelPart, Boolean, T> constructor) { BiFunction<ModelPart, Boolean, T> constructor) {
return registerPlayer(name, race, constructor, PlayerPonyRenderer::new); return registerPlayer(name, race, constructor, PonyArmourModel::new);
}
static <E extends LivingEntity, T extends Model & MsonModel & IModel> PlayerModelKey<E, T> registerPlayer(String name, Race race,
BiFunction<ModelPart, Boolean, T> constructor,
PlayerModelKey.RendererFactory rendererFactory) {
return registerPlayer(name, race, constructor, PonyArmourModel::new, rendererFactory);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <E extends LivingEntity, T extends Model & MsonModel & IModel> PlayerModelKey<E, T> registerPlayer(String name, Race race, static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<E, T> registerPlayer(String name, Race race,
BiFunction<ModelPart, Boolean, T> constructor, BiFunction<ModelPart, Boolean, T> constructor,
MsonModel.Factory<PonyArmourModel<E>> armorFactory, MsonModel.Factory<PonyArmourModel<E>> armorFactory) {
PlayerModelKey.RendererFactory rendererFactory) { return (PlayerModelKey<E, T>)PLAYER_MODELS.computeIfAbsent(race, r -> new PlayerModelKey<>(name, constructor, armorFactory));
return (PlayerModelKey<E, T>)PLAYER_MODELS.computeIfAbsent(race, r -> {
return new PlayerModelKey<>(name, constructor, rendererFactory, armorFactory);
});
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends AbstractGear> GearModelKey<T> registerGear(String name, Wearable wearable, MsonModel.Factory<T> constructor) { static <T extends AbstractGearModel> GearModelKey<T> registerGear(String name, Wearable wearable, MsonModel.Factory<T> constructor) {
return (GearModelKey<T>)GEAR_MODELS.computeIfAbsent(wearable, w -> { return (GearModelKey<T>)GEAR_MODELS.computeIfAbsent(wearable, w -> {
return new GearModelKey<T>(Mson.getInstance().registerModel(new Identifier("minelittlepony", "gear/" + name), constructor), constructor); return new GearModelKey<T>(Mson.getInstance().registerModel(new Identifier("minelittlepony", "gear/" + name), constructor), constructor);
}); });
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends AbstractGear> GearModelKey<T> registerGear(GearModelKey<T> key, Wearable wearable, MsonModel.Factory<T> constructor) { static <T extends AbstractGearModel> GearModelKey<T> registerGear(GearModelKey<T> key, Wearable wearable, MsonModel.Factory<T> constructor) {
return (GearModelKey<T>)GEAR_MODELS.computeIfAbsent(wearable, w -> new GearModelKey<T>(key.key, constructor)); return (GearModelKey<T>)GEAR_MODELS.computeIfAbsent(wearable, w -> new GearModelKey<T>(key.key, constructor));
} }
static <T extends Model> ModelKey<T> register(String name, MsonModel.Factory<T> constructor) { static <T extends Model> ModelKey<T> register(String name, MsonModel.Factory<T> constructor) {
return Mson.getInstance().registerModel(new Identifier("minelittlepony", name), constructor); return new ModelKeyImpl<T>(new Identifier("minelittlepony", name), constructor);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Nullable @Nullable
public static <E extends LivingEntity, T extends Model & MsonModel & IModel> PlayerModelKey<E, T> getPlayerModel(Race race) { public static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<E, T> getPlayerModel(Race race) {
return (PlayerModelKey<E, T>)PLAYER_MODELS.get(race); return (PlayerModelKey<E, T>)PLAYER_MODELS.get(race);
} }
public static Stream<Map.Entry<Wearable, GearModelKey<? extends IGear>>> getWearables() { public static Stream<Map.Entry<Wearable, GearModelKey<? extends Gear>>> getWearables() {
return GEAR_MODELS.entrySet().stream(); return GEAR_MODELS.entrySet().stream();
} }
public static void bootstrap() { } public static void bootstrap() { }
public record GearModelKey<T extends IGear>(ModelKey<T> key, MsonModel.Factory<T> constructor) { public record GearModelKey<T extends Gear>(ModelKey<T> key, MsonModel.Factory<T> constructor) {
public T createModel() { public T createModel() {
return key.createModel(constructor); return key.createModel(constructor);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,10 +7,10 @@ import net.minecraft.client.render.entity.model.GuardianEntityModel;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.mob.GuardianEntity; import net.minecraft.entity.mob.GuardianEntity;
import com.minelittlepony.client.model.IPonyMixinModel; import com.minelittlepony.api.model.PonyModelMixin;
import com.minelittlepony.client.model.entity.race.SeaponyModel; import com.minelittlepony.client.model.entity.race.SeaponyModel;
public class GuardianPonyModel extends GuardianEntityModel implements IPonyMixinModel.Caster<GuardianEntity, SeaponyModel<GuardianEntity>, ModelPart> { public class GuardianPonyModel extends GuardianEntityModel implements PonyModelMixin.Caster<GuardianEntity, SeaponyModel<GuardianEntity>, ModelPart> {
private final SeaponyModel<GuardianEntity> mixin; private final SeaponyModel<GuardianEntity> mixin;
public GuardianPonyModel(ModelPart tree) { public GuardianPonyModel(ModelPart tree) {
@ -18,28 +18,29 @@ public class GuardianPonyModel extends GuardianEntityModel implements IPonyMixin
mixin = new SeaponyModel<>(tree); mixin = new SeaponyModel<>(tree);
} }
@Override
public void setAngles(GuardianEntity entity, float move, float swing, float ticks, float headYaw, float headPitch) {
mixin().setAngles(entity, move, swing, ticks, headYaw, headPitch);
}
@Override
public void render(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float limbDistance, float limbAngle, float tickDelta, float alpha) {
mixin().render(stack, vertices, overlayUv, lightUv, limbDistance, limbAngle, tickDelta, alpha);
}
@Override
public void animateModel(GuardianEntity entity, float move, float swing, float float_3) {
mixin().animateModel(entity, move, swing, float_3);
}
@Override
public void copyStateTo(EntityModel<GuardianEntity> model) {
mixin().copyStateTo(model);
}
@Override @Override
public SeaponyModel<GuardianEntity> mixin() { public SeaponyModel<GuardianEntity> mixin() {
return mixin; return mixin;
} }
@Override
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) {
mixin().render(matrices, vertices, light, overlay, red, green, blue, alpha);
}
@Override
public void animateModel(GuardianEntity entity, float limbAngle, float limbDistance, float tickDelta) {
mixin().animateModel(entity, limbAngle, limbDistance, tickDelta);
}
@Override
public void copyStateTo(EntityModel<GuardianEntity> copy) {
mixin().copyStateTo(copy);
}
@Override
public void setAngles(GuardianEntity entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) {
mixin().setVisible(true);
mixin().setAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch);
}
} }

View file

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

View file

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

View file

@ -1,12 +1,15 @@
package com.minelittlepony.client.model.entity; package com.minelittlepony.client.model.entity;
import net.minecraft.client.model.ModelPart; import net.minecraft.client.model.ModelPart;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.mob.WitchEntity; import net.minecraft.entity.mob.WitchEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;
import com.minelittlepony.api.model.ModelAttributes; import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.pony.IPony; import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.client.model.entity.race.EarthPonyModel; import com.minelittlepony.client.model.entity.race.EarthPonyModel;
public class WitchPonyModel extends EarthPonyModel<WitchEntity> { public class WitchPonyModel extends EarthPonyModel<WitchEntity> {
@ -16,7 +19,7 @@ public class WitchPonyModel extends EarthPonyModel<WitchEntity> {
} }
@Override @Override
public void updateLivingState(WitchEntity entity, IPony pony, ModelAttributes.Mode mode) { public void updateLivingState(WitchEntity entity, Pony pony, ModelAttributes.Mode mode) {
super.updateLivingState(entity, pony, mode); super.updateLivingState(entity, pony, mode);
if (entity.hasCustomName() && "Filly".equals(entity.getCustomName().getString())) { if (entity.hasCustomName() && "Filly".equals(entity.getCustomName().getString())) {
@ -60,11 +63,14 @@ public class WitchPonyModel extends EarthPonyModel<WitchEntity> {
} }
} }
@Override
protected void positionheldItem(Arm arm, MatrixStack matrices) {
super.positionheldItem(arm, matrices);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(10));
}
@Override @Override
public boolean isWearing(Wearable wearable) { public boolean isWearing(Wearable wearable) {
if (wearable == Wearable.HAT) { return wearable == Wearable.HAT || super.isWearing(wearable);
return true;
}
return super.isWearing(wearable);
} }
} }

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
package com.minelittlepony.client.model.entity.race; package com.minelittlepony.client.model.entity.race;
import com.minelittlepony.api.model.IPart; import com.minelittlepony.api.model.SubModel;
import com.minelittlepony.client.model.AbstractPonyModel; import com.minelittlepony.client.model.AbstractPonyModel;
import com.minelittlepony.client.model.part.*; import com.minelittlepony.client.model.part.*;
import com.minelittlepony.mson.api.ModelView; import com.minelittlepony.mson.api.ModelView;
@ -12,12 +12,19 @@ public class EarthPonyModel<T extends LivingEntity> extends AbstractPonyModel<T>
private final boolean smallArms; private final boolean smallArms;
protected IPart tail; protected SubModel tail;
protected PonySnout snout; protected PonySnout snout;
protected PonyEars ears; protected PonyEars ears;
private final ModelPart mane;
private final ModelPart nose;
private final ModelPart tailStub;
public EarthPonyModel(ModelPart tree, boolean smallArms) { public EarthPonyModel(ModelPart tree, boolean smallArms) {
super(tree); super(tree);
mane = neck.getChild("mane");
nose = head.getChild("nose");
tailStub = body.getChild("tail_stub");
this.smallArms = smallArms; this.smallArms = smallArms;
} }
@ -45,4 +52,12 @@ public class EarthPonyModel<T extends LivingEntity> extends AbstractPonyModel<T>
} }
return super.getLegOutset(); return super.getLegOutset();
} }
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
mane.visible = attributes.isHorsey;
nose.visible = attributes.isHorsey;
tailStub.visible = !attributes.isHorsey;
}
} }

View file

@ -0,0 +1,23 @@
package com.minelittlepony.client.model.entity.race;
import net.minecraft.client.model.ModelPart;
import net.minecraft.entity.LivingEntity;
import com.minelittlepony.api.model.Pivot;
public class KirinModel<T extends LivingEntity> extends UnicornModel<T> {
private final ModelPart beard;
public KirinModel(ModelPart tree, boolean smallArms) {
super(tree, smallArms);
beard = neck.getChild("beard");
}
@Override
protected void adjustBody(float pitch, Pivot pivot) {
super.adjustBody(pitch, pivot);
beard.resetTransform();
beard.pitch -= neck.pitch;
}
}

View file

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

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