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
[![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)
![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)
![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)
Turns players and mobs into ponies.

View file

@ -1,16 +1,15 @@
buildscript {
dependencies {
classpath 'com.github.dexman545:Outlet:1.3.10'
classpath 'com.github.dexman545:Outlet:1.6.1'
}
}
plugins {
id 'java-library'
id 'fabric-loom' version '0.12-SNAPSHOT'
id 'fabric-loom' version '1.5-SNAPSHOT'
id 'maven-publish'
id 'com.modrinth.minotaur' version '2.+'
id 'org.ajoberstar.reckon' version '0.13.0'
id 'org.ajoberstar.reckon' version '0.13.1'
}
apply plugin: 'dex.plugins.outlet'
apply plugin: 'io.github.dexman545.outlet'
java {
toolchain {
@ -38,18 +37,9 @@ reckon {
repositories {
mavenLocal()
maven {
name = 'modmenu'
url = 'https://maven.terraformersmc.com/releases'
}
maven {
name = 'minelp'
url = 'https://repo.minelittlepony-mod.com/maven/snapshot'
}
maven {
name = 'minelp-release'
url = 'https://repo.minelittlepony-mod.com/maven/release'
}
maven { name 'modmenu'; url 'https://maven.terraformersmc.com/releases' }
maven { name 'minelp'; url 'https://repo.minelittlepony-mod.com/maven/snapshot' }
maven { name 'minelp-release'; url 'https://repo.minelittlepony-mod.com/maven/release' }
}
dependencies {
@ -69,7 +59,7 @@ dependencies {
include "com.minelittlepony:mson:${project.mson_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()
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'
@ -122,6 +117,7 @@ modrinth {
versionNumber = version.toString()
versionName = archivesBaseName + '-' + version
changelog = "[Changelog](https://github.com/MineLittlePony/MineLittlePony/releases/tag/${version.toString()})"
loaders = ['fabric', 'quilt']
uploadFile = remapJar
outlet.mcVersions().each{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
# check these on https://fabricmc.net/develop
minecraft_version=1.19.4
yarn_mappings=1.19.4+build.1
loader_version=0.14.17
fabric_version=0.76.0+1.19.4
minecraft_version=1.20.2
yarn_mappings=1.20.2+build.4
loader_version=0.15.1
fabric_version=0.91.1+1.20.2
# Mod Properties
group=com.minelittlepony
@ -15,12 +15,12 @@ org.gradle.daemon=false
description=Mine Little Pony turns players and mobs into ponies. Press F9 ingame to access settings.
# Publishing
minecraft_version_range=>=1.19.4
minecraft_version_range=>=1.20.2
modrinth_loader_type=fabric
modrinth_project_id=JBjInUXM
# Dependencies
modmenu_version=6.1.0-rc.4
kirin_version=1.14.0
hd_skins_version=6.8.0
mson_version=1.8.0
modmenu_version=8.0.0
kirin_version=1.16.1+1.20.2
hd_skins_version=6.12.4+1.20.2
mson_version=1.9.3+1.20.2

View file

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

View file

@ -1,14 +1,8 @@
pluginManagement {
repositories {
mavenCentral()
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
maven {
name = 'Jitpack'
url = 'https://jitpack.io'
}
maven { name 'Fabric'; url 'https://maven.fabricmc.net/' }
maven { name 'Jitpack'; url 'https://jitpack.io' }
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 com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.pony.meta.Sizes;
import com.minelittlepony.api.pony.meta.*;
import com.minelittlepony.common.client.gui.VisibilityMode;
import com.minelittlepony.common.util.GamePaths;
import com.minelittlepony.common.util.settings.*;
@ -52,8 +52,15 @@ public class PonyConfig extends Config {
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("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("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)
.addComment("Pegasi will use their wings to fly even when they're wearing an elytra");
public final Setting<Boolean> noFun = value("customisation", "noFun", false)
.addComment("Disables certain easter eggs and secrets (party pooper)")
.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) {
super(HEIRARCHICAL_JSON_ADAPTER, path);
instance = this;
@ -129,4 +143,18 @@ public class PonyConfig extends Config {
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.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.ServerPlayNetworking;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.Identifier;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.network.MsgPonyData;
import com.minelittlepony.client.MineLittlePony;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.minelittlepony.api.pony.PonyData;
@Environment(EnvType.CLIENT)
public class Channel {
private static final Identifier CLIENT_PONY_DATA = new Identifier("minelittlepony", "pony_data");
private static final Identifier REQUEST_PONY_DATA = new Identifier("minelittlepony", "request_pony_data");
private static final Logger LOGGER = LogManager.getLogger("MineLittlePony:Networking");
private static boolean registered;
public static void bootstrap() {
ClientLoginConnectionEvents.INIT.register((handler, client) -> {
registered = false;
MineLittlePony.logger.info("Resetting registered flag");
});
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());
});
ClientPlayNetworking.registerGlobalReceiver(REQUEST_PONY_DATA, (client, handler, ignored, sender) -> {
if (client.player != null) {
IPony pony = IPony.getManager().getPony(client.player);
registered = true;
MineLittlePony.logger.info("Server has just consented");
sender.sendPacket(CLIENT_PONY_DATA, new MsgPonyData(pony.metadata(), pony.defaulted()).toBuffer(PacketByteBufs.create()));
}
LOGGER.info("Server has just consented");
});
ServerPlayNetworking.registerGlobalReceiver(CLIENT_PONY_DATA, (server, player, ignore, buffer, ignore2) -> {
MsgPonyData packet = new MsgPonyData(buffer);
PonyData packet = MsgPonyData.read(buffer);
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) {
throw new RuntimeException("Client packet send called by the server");
}
if (!registered) {
if (MinecraftClient.getInstance().isInSingleplayer() || MinecraftClient.getInstance().isIntegratedServerRunning()) {
MineLittlePony.logger.info("Sending pony skin data over as we are either in single-player or lan");
} else {
MineLittlePony.logger.info("Skipping network packet as the server has not consented");
return;
}
}
ClientPlayNetworking.send(CLIENT_PONY_DATA, packet.toBuffer(PacketByteBufs.create()));
ClientPlayNetworking.send(CLIENT_PONY_DATA, MsgPonyData.write(packet, PacketByteBufs.create()));
return true;
}
}

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.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.entity.player.PlayerEntity;
import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.api.pony.PonyData;
/**
* Callback triggered on the server when receiving pony data from a client.
*
*/
public interface PonyDataCallback {
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() + "!");
Event<PonyDataCallback> EVENT = EventFactory.createArrayBacked(PonyDataCallback.class, listeners -> (sender, data, env) -> {
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.
* @param sender The player who sent the data - this is the owner of 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.
*/
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.EventFactory;
import net.minecraft.entity.Entity;
import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.api.model.ModelAttributes;
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.util.Arm;
@ -6,10 +6,7 @@ import net.minecraft.util.math.MathHelper;
import com.minelittlepony.mson.util.PartUtil;
/**
* Common interface for all undead enemies.
*/
public interface IMobModel {
public final class MobPosingHelper {
/**
* 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 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 roll = MathHelper.sin((1 - (1 - swingProgress) * (1 - swingProgress)) * MathHelper.PI);
@ -33,20 +30,26 @@ public interface IMobModel {
arm.roll = cos;
}
static void rotateUndeadArms(ClientPonyModel<?> model, float move, float ticks) {
ModelPart leftArm = model.getArm(Arm.LEFT);
ModelPart rightArm = model.getArm(Arm.RIGHT);
public static void rotateUndeadArms(PonyModel<?> model, float move, float ticks) {
ModelPart leftArm = model.getForeLeg(Arm.LEFT);
ModelPart rightArm = model.getForeLeg(Arm.RIGHT);
if (islookAngleRight(move)) {
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);
} 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);
}
}
static boolean islookAngleRight(float move) {
public static boolean islookAngleRight(float move) {
return MathHelper.sin(move / 20) < 0;
}
}

View file

@ -1,9 +1,7 @@
package com.minelittlepony.api.model;
import com.minelittlepony.api.config.PonyConfig;
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.util.MathUtil;
@ -11,8 +9,9 @@ import java.util.*;
import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm;
import net.minecraft.util.Identifier;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.*;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
@ -21,6 +20,11 @@ public class ModelAttributes {
* True if the model is sleeping in a bed.
*/
public boolean isSleeping;
/**
* True if the model is lying down comfortably
*/
public boolean isLyingDown;
/**
* 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).
*/
public boolean isGoingFast;
/**
* Flag indicating that this model should mimic the vanilla horse models.
*/
public boolean isHorsey;
/**
* Vertical pitch whilst flying.
@ -105,16 +113,21 @@ public class ModelAttributes {
/**
* 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.
*/
public void checkRainboom(LivingEntity entity, boolean hasWings, float ticks) {
public void checkRainboom(LivingEntity entity, PonyModel<?> model, float ticks) {
Vec3d motion = entity.getVelocity();
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 |= entity.isUsingRiptide();
isGoingFast |= entity.isFallFlying();
@ -129,31 +142,41 @@ public class ModelAttributes {
return (MathHelper.sin(ticks * 0.136f) / 2) + MathUtil.Angles._270_DEG;
}
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;
isSitting = PonyPosture.isSitting(entity);
isCrouching = !isSitting && mode == Mode.THIRD_PERSON && PonyPosture.isCrouching(pony, entity);
isSleeping = entity.isAlive() && entity.isSleeping();
isFlying = mode == Mode.THIRD_PERSON && PonyPosture.isFlying(entity);
isSleeping = entity.isAlive() && entity.isSleeping();;
isLyingDown = isSleeping;
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();
isSwimming = mode == Mode.THIRD_PERSON && PonyPosture.isSwimming(entity);
isSwimmingRotated = isSwimming;
isRiptide = entity.isUsingRiptide();
isRidingInteractive = PonyPosture.isRidingAPony(entity);
if (!(entity instanceof IPreviewModel)) {
if (!(entity instanceof PreviewModel)) {
interpolatorId = entity.getUuid();
}
isLeftHanded = entity.getMainArm() == Arm.LEFT;
isHorsey = PonyConfig.getInstance().horsieMode.get();
featureSkins = SkinsProxy.instance.getAvailableSkins(entity);
mainArm = entity.getMainArm();
activeHand = entity.getActiveHand();
itemUseTime = entity.getItemUseTimeLeft();
}
public Interpolator getMainInterpolator() {
return metadata.getInterpolator(interpolatorId);
return Interpolator.linear(interpolatorId);
}
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.item.ArmorItem;
@ -6,12 +6,11 @@ import net.minecraft.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.model.IModelWrapper;
import com.minelittlepony.api.model.armour.*;
import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.mson.api.*;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.client.model.PlayerModelKey;
import com.minelittlepony.client.model.armour.*;
import com.minelittlepony.mson.api.ModelKey;
import com.minelittlepony.mson.api.MsonModel;
import java.util.*;
import java.util.function.Consumer;
@ -19,14 +18,14 @@ import java.util.function.Consumer;
/**
* 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
private final MsonModel.Factory<PonyArmourModel<T>> armorFactory;
private final Map<ModelKey<PonyArmourModel<?>>, PonyArmourModel<T>> armor = new HashMap<>();
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.body = playerModelKey.getKey(slimArms).createModel();
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.body = key.createModel();
}
@ -50,8 +49,7 @@ public class ModelWrapper<T extends LivingEntity, M extends IModel> implements I
}));
}
@Override
public ModelWrapper<T, M> applyMetadata(IPonyData meta) {
public Models<T, M> applyMetadata(PonyData meta) {
body.setMetadata(meta);
armor.values().forEach(a -> a.setMetadata(meta));
return this;

View file

@ -1,66 +1,49 @@
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.entity.LivingEntity;
import net.minecraft.util.math.MathHelper;
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.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.
*/
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.
*/
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.
*/
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() {
return getAttributes().isFlying && canFly();
}
/**
* Returns true if this model is riding a boat, horse, or other animals.
*
* @deprecated User model#getAttributes().isSitting
*/
@Deprecated
default boolean isRiding() {
return getAttributes().isSitting;
default Size getSize() {
return PonyConfig.getEffectiveSize(getAttributes().metadata.size());
}
default Race getRace() {
return PonyConfig.getEffectiveRace(getMetadata().getRace());
}
/**
* Returns true if this model is being applied to a race that has wings.
*/
default boolean canFly() {
return getRace().hasWings();
return PonyConfig.getEffectiveRace(getAttributes().metadata.race());
}
/**
@ -72,7 +55,6 @@ public interface IModel {
* Gets the step wobble used for various hair bits and animations.
*/
default float getWobbleAmount() {
if (getSwingAmount() <= 0) {
return 0;
}
@ -97,6 +79,7 @@ public interface IModel {
* i.e. Used to change wing rendering when using saddlebags.
*/
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.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.ModelWithArms;
import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm;
import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.model.IUnicorn;
import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.api.pony.meta.Size;
import com.minelittlepony.mson.api.ModelView;
import com.minelittlepony.mson.api.model.BoxBuilder.RenderLayerSetter;
public interface 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();
@Override
@ -29,7 +26,7 @@ public interface IPonyMixinModel<T extends LivingEntity, M extends IPonyModel<T>
}
@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);
}
@ -54,7 +51,7 @@ public interface IPonyMixinModel<T extends LivingEntity, M extends IPonyModel<T>
}
@Override
default void setMetadata(IPonyData meta) {
default void setMetadata(PonyData meta) {
mixin().setMetadata(meta);
}
@ -73,6 +70,22 @@ public interface IPonyMixinModel<T extends LivingEntity, M extends IPonyModel<T>
return mixin().getRiderYOffset();
}
@Override
default ModelPart getForeLeg(Arm side) {
return mixin().getForeLeg(side);
}
@Override
default ModelPart getHindLeg(Arm side) {
return mixin().getHindLeg(side);
}
@Override
default ArmPose getArmPoseForSide(Arm side) {
return mixin().getArmPoseForSide(side);
}
@Override
default void setArmAngle(Arm arm, MatrixStack stack) {
if (mixin() instanceof ModelWithArms) {
@ -90,7 +103,12 @@ public interface IPonyMixinModel<T extends LivingEntity, M extends IPonyModel<T>
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
default boolean 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.util.math.MatrixStack;
public interface IPart {
public interface SubModel {
/**
* 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.
*/
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.

View file

@ -1,10 +1,12 @@
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.client.MineLittlePony;
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_FULL_SPREAD_ANGLE = MathUtil.Angles._270_DEG + 0.4F;
public static final float WINGS_RAISED_ANGLE = 4;
@ -13,18 +15,20 @@ public interface IPegasus extends IModel {
* Returns true if the wings are spread.
*/
default boolean wingsAreOpen() {
return (getAttributes().isSwimming || isFlying() || getAttributes().isCrouching)
&& (MineLittlePony.getInstance().getConfig().flappyElytras.get() || !getAttributes().isGliding);
return (getAttributes().isSwimming || getAttributes().isFlying || getAttributes().isCrouching)
&& (PonyConfig.getInstance().flappyElytras.get() || !getAttributes().isGliding);
}
default boolean isBurdened() {
return isWearing(Wearable.SADDLE_BAGS_BOTH) || isWearing(Wearable.SADDLE_BAGS_LEFT) || isWearing(Wearable.SADDLE_BAGS_RIGHT);
return isWearing(Wearable.SADDLE_BAGS_BOTH)
|| isWearing(Wearable.SADDLE_BAGS_LEFT)
|| isWearing(Wearable.SADDLE_BAGS_RIGHT);
}
/**
* Gets the wings of this pegasus/flying creature
*/
IPart getWings();
SubModel getWings();
/**
* 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.ModelPart;
@ -6,22 +6,24 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import com.minelittlepony.api.model.gear.IGear;
import java.util.ArrayList;
import java.util.List;
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<>();
public AbstractGear() {
private final float stackingHeight;
public AbstractGearModel(float stackingHeight) {
super(RenderLayer::getEntitySolid);
this.stackingHeight = stackingHeight;
}
public void addPart(ModelPart t) {
public AbstractGearModel addPart(ModelPart t) {
parts.add(t);
return this;
}
@Override
@ -35,4 +37,14 @@ public abstract class AbstractGear extends Model implements IGear {
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.util.Identifier;
import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.model.*;
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;
/**
* 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.
* <p>
* This would be awesome for creating socks.
*/
static Supplier<IGear> register(Supplier<IGear> gear) {
GearFeature.addModGear(gear);
static Supplier<Gear> register(Supplier<Gear> gear) {
MOD_GEARS.add(gear);
return gear;
}
@ -38,13 +37,26 @@ public interface IGear {
*
* @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.
*/
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.
*
@ -62,7 +74,7 @@ public interface IGear {
/**
* 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();
model.transform(part, matrices);
model.getBodyPart(part).rotate(matrices);
@ -73,23 +85,10 @@ public interface IGear {
*
* 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) {
setModelAttributes(model, entity);
pose(rainboom, interpolatorId, move, swing, bodySwing, ticks);
default void pose(PonyModel<?> model, Entity entity, boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float 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.
*/
@ -101,7 +100,7 @@ public interface IGear {
* @param <T> The type of entity being rendered.
* @param <M> The type of the entity's primary model.
*/
public interface Context<T extends Entity, M extends IModel> {
public interface Context<T extends Entity, M extends PonyModel<?>> {
/**
* 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.
*/
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);
}

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.util.Identifier;
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;
public abstract class AbstractWearableGear extends AbstractGear {
public class WearableGear extends AbstractGearModel {
protected final Wearable wearable;
protected final BodyPart location;
protected AbstractWearableGear(Wearable wearable, BodyPart location) {
public WearableGear(Wearable wearable, BodyPart location, float stackingHeight) {
super(stackingHeight);
this.wearable = wearable;
this.location = location;
}
@ -23,7 +24,7 @@ public abstract class AbstractWearableGear extends AbstractGear {
}
@Override
public boolean canRender(IModel model, Entity entity) {
public boolean canRender(PonyModel<?> model, Entity entity) {
return model.isWearing(wearable);
}

View file

@ -1,20 +1,43 @@
package com.minelittlepony.api.pony;
import net.minecraft.client.util.DefaultSkinHelper;
import net.minecraft.client.util.SkinTextures;
import net.minecraft.util.Identifier;
import java.util.HashMap;
import java.util.Map;
import com.minelittlepony.api.pony.meta.Race;
import java.util.*;
public final class DefaultPonySkinHelper {
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) {
return SKINS.computeIfAbsent(original, DefaultPonySkinHelper::computePonySkin);
private static final Map<SkinTextures, SkinTextures> SKINS = new HashMap<>();
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) {
return new Identifier("minelittlepony", original.getPath().replace(".png", "_pony.png"));
public static String getModelType(UUID id) {
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;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
@ -10,16 +11,33 @@ import java.util.Optional;
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.
*
* 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.
@ -27,14 +45,7 @@ public interface IPonyManager {
*
* @param player the player
*/
IPony getPony(PlayerEntity player);
/**
* Gets or creates a pony for the given skin resource and vanilla model type.
*
* @param resource A texture resource
*/
IPony getPony(Identifier resource);
Pony getPony(PlayerEntity player);
/**
* 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 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 uuid A UUID. Either a user or an entity.
* @param resource A texture resource
*/
IPony getBackgroundPony(UUID uuid);
/**
* De-registers a pony from the cache.
*/
void removePony(Identifier resource);
default Pony getPony(Identifier resource) {
return getPony(resource, null);
}
interface ForcedPony {}
final class Instance {
public static IPonyManager instance;
public static PonyManager instance;
}
}

View file

@ -1,25 +1,27 @@
package com.minelittlepony.api.pony;
import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.model.PreviewModel;
import java.util.Optional;
import net.minecraft.block.BlockState;
import net.minecraft.block.Material;
import net.minecraft.block.StairsBlock;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.registry.tag.FluidTags;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public final class PonyPosture {
public static Optional<IPony> getMountPony(LivingEntity entity) {
public static Optional<Pony> getMountPony(LivingEntity entity) {
return entity.getVehicle() instanceof LivingEntity mount
? IPony.getManager().getPony(mount)
? Pony.getManager().getPony(mount)
: Optional.empty();
}
public static boolean isCrouching(IPony pony, LivingEntity entity) {
public static boolean isCrouching(Pony pony, LivingEntity entity) {
boolean isSneak = entity.isInSneakingPose();
boolean isFlying = isFlying(entity);
boolean isSwimming = isSwimming(entity);
@ -27,7 +29,7 @@ public final class PonyPosture {
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();
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) {
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) {
@ -81,6 +83,43 @@ public final class PonyPosture {
}
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;
import com.minelittlepony.api.pony.TriggerPixelType;
public enum Gender implements TriggerPixelType<Gender> {
public enum Gender implements TValue<Gender> {
MARE(0),
STALLION(0xffffff),
ABOMONATION(0x888888);
@ -14,7 +12,7 @@ public enum Gender implements TriggerPixelType<Gender> {
}
@Override
public int getColorCode() {
public int colorCode() {
return triggerValue;
}

View file

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

View file

@ -1,17 +1,15 @@
package com.minelittlepony.api.pony.meta;
import com.minelittlepony.api.pony.TriggerPixelType;
/**
* 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.
*
*/
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.
*/
@ -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.
*/
float getShadowSize();
float shadowSize();
/**
* The global scale factor applied to all physical dimensions.
*/
float getScaleFactor();
float scaleFactor();
/**
* A scale factor used to alter the vertical eye position.
*/
float getEyeHeightFactor();
float eyeHeightFactor();
/**
* 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.
*/
public enum Sizes implements Size {
public enum SizePreset implements Size {
TALL (0x534b76, 0.45f, 1.1F, 1.15F),
BULKY (0xce3254, 0.5f, 1, 1.05F),
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),
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;
private float shadowSize;
private float scale;
private float camera;
Sizes(int pixel, float shadowSz, float scaleF, float cameraF) {
SizePreset(int pixel, float shadowSz, float scaleF, float cameraF) {
triggerValue = pixel;
shadowSize = shadowSz;
scale = scaleF;
@ -34,17 +31,22 @@ public enum Sizes implements Size {
}
@Override
public float getShadowSize() {
public int colorCode() {
return triggerValue;
}
@Override
public float shadowSize() {
return shadowSize * PonyConfig.getInstance().getGlobalScaleFactor();
}
@Override
public float getScaleFactor() {
public float scaleFactor() {
return scale * PonyConfig.getInstance().getGlobalScaleFactor();
}
@Override
public float getEyeHeightFactor() {
public float eyeHeightFactor() {
if (!PonyConfig.getInstance().fillycam.get()) {
return 1;
}
@ -52,26 +54,7 @@ public enum Sizes implements Size {
}
@Override
public float getEyeDistanceFactor() {
if (!PonyConfig.getInstance().fillycam.get()) {
return 1;
}
return camera * PonyConfig.getInstance().getGlobalScaleFactor();
}
@Override
public int getColorCode() {
return triggerValue;
}
public static Sizes of(Size size) {
if (size instanceof Sizes) {
return (Sizes)size;
}
int i = size.ordinal();
if (i < 0 || i >= REGISTRY.length) {
return UNSET;
}
return REGISTRY[i];
public float eyeDistanceFactor() {
return eyeHeightFactor();
}
}

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

View file

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

View file

@ -1,115 +1,92 @@
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 java.util.Arrays;
import java.util.*;
/**
* Individual trigger pixels for a pony skin.
*
*/
@SuppressWarnings("unchecked")
public enum TriggerPixel {
RACE(Race.HUMAN, Channel.ALL, 0, 0),
TAIL(TailLength.FULL, Channel.ALL, 1, 0),
GENDER(Gender.MARE, Channel.ALL, 2, 0),
SIZE(Sizes.NORMAL, Channel.ALL, 3, 0),
GLOW(null, Channel.RAW, 0, 1),
WEARABLES(Wearable.NONE, Channel.RAW, 1, 1),
TAIL_SHAPE(TailShape.STRAIGHT, Channel.ALL, 2, 1);
public interface TriggerPixel<T> {
Vector2i MAX_COORDS = new Vector2i();
private int x;
private int y;
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);
private Channel channel;
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));
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;
if (ColorHelper.Argb.getAlpha(color) < 255) {
return (T)def;
}
return lookup.getOrDefault(color & 0x00FFFFFF, def);
};
}
/**
* Reads this trigger pixel's value and returns the raw colour.
*
* @param image Image to read
*/
public int readColor(NativeImage image) {
return channel.readValue(x, y, image);
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));
}
/**
* 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) {
return new TriggerPixelValue<>(color, (T)def);
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")
Set<T> values = EnumSet.noneOf((Class<T>)def.def().getClass());
if (flagReader.readFlag(ColorHelper.Argb.getRed(color), values)
| flagReader.readFlag(ColorHelper.Argb.getGreen(color), values)
| flagReader.readFlag(ColorHelper.Argb.getBlue(color), values)) {
return new Flags<>(def.def(), values, color & 0x00FFFFFF);
}
return def;
};
}
return new TriggerPixelValue<>(color, TriggerPixelType.getByTriggerPixel((T)def, color));
static <T extends TValue<T>> Int2ObjectOpenHashMap<T> buildLookup(T[] options) {
Int2ObjectOpenHashMap<T> lookup = new Int2ObjectOpenHashMap<>();
for (int i = 0; i < options.length; i++) {
lookup.put(options[i].colorCode(), options[i]);
}
return lookup;
}
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);
T read(Mat image);
static boolean isTriggerPixelCoord(int x, int y) {
return x <= MAX_COORDS.x && y <= MAX_COORDS.y;
}
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);
interface Mat {
int getColor(int x, int y);
}
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;
import net.minecraft.util.Identifier;
import com.minelittlepony.api.pony.TriggerPixelType;
import com.minelittlepony.client.model.gear.SaddleBags;
import com.minelittlepony.common.util.Color;
import net.minecraft.util.math.ColorHelper;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public enum Wearable implements TriggerPixelType<Wearable> {
public enum Wearable implements TValue<Wearable> {
NONE (0x00, null),
CROWN (0x16, new Identifier("minelittlepony", "textures/models/crown.png")),
MUFFIN (0x32, new Identifier("minelittlepony", "textures/models/muffin.png")),
HAT (0x64, new Identifier("textures/entity/witch.png")),
ANTLERS (0x96, new Identifier("minelittlepony", "textures/models/antlers.png")),
SADDLE_BAGS_LEFT (0xC6, SaddleBags.TEXTURE),
SADDLE_BAGS_RIGHT (0xC7, SaddleBags.TEXTURE),
SADDLE_BAGS_BOTH (0xC8, SaddleBags.TEXTURE),
SADDLE_BAGS_LEFT (0xC6, new Identifier("minelittlepony", "textures/models/saddlebags.png")),
SADDLE_BAGS_RIGHT (0xC7, new Identifier("minelittlepony", "textures/models/saddlebags.png")),
SADDLE_BAGS_BOTH (0xC8, new Identifier("minelittlepony", "textures/models/saddlebags.png")),
STETSON (0xFA, new Identifier("minelittlepony", "textures/models/stetson.png"));
private int triggerValue;
private final Identifier id;
private final Identifier texture;
public static final List<Wearable> VALUES = Arrays.stream(values()).toList();
public static final Map<Identifier, Wearable> REGISTRY = VALUES.stream().collect(Collectors.toMap(Wearable::getId, Function.identity()));
public static final Map<Identifier, Wearable> REGISTRY = Arrays.stream(values()).collect(Collectors.toMap(Wearable::getId, Function.identity()));
public static final Flags<Wearable> EMPTY_FLAGS = Flags.of(NONE);
Wearable(int pixel, Identifier texture) {
triggerValue = pixel;
@ -45,7 +42,7 @@ public enum Wearable implements TriggerPixelType<Wearable> {
}
@Override
public int getColorCode() {
public int colorCode() {
return triggerValue;
}
@ -55,22 +52,6 @@ public enum Wearable implements TriggerPixelType<Wearable> {
@Override
public int getChannelAdjustedColorCode() {
return triggerValue == 0 ? 0 : Color.argbToHex(255, triggerValue, triggerValue, triggerValue);
}
public static boolean[] flags(Wearable[] wears) {
boolean[] flags = new boolean[VALUES.size()];
for (int i = 0; i < wears.length; i++) {
flags[wears[i].ordinal()] = true;
}
return flags;
}
public static Wearable[] flags(boolean[] flags) {
List<Wearable> wears = new ArrayList<>();
for (int i = 0; i < VALUES.size(); i++) {
if (flags[i]) wears.add(VALUES.get(i));
}
return wears.toArray(new Wearable[0]);
return triggerValue == 0 ? 0 : ColorHelper.Argb.getArgb(255, triggerValue, triggerValue, triggerValue);
}
}

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.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;
public class HorseCam {
@ -31,7 +32,7 @@ public class HorseCam {
*/
public static float transformCameraAngle(float pitch) {
if (!MineLittlePony.getInstance().getConfig().fillycam.get()) {
if (!PonyConfig.getInstance().fillycam.get()) {
return pitch;
}
@ -48,10 +49,10 @@ public class HorseCam {
return pitch;
}
IPony pony = IPony.getManager().getPony(player);
Pony pony = Pony.getManager().getPony(player);
if (!pony.race().isHuman()) {
Setting<Boolean> fillyCam = MineLittlePony.getInstance().getConfig().fillycam;
Setting<Boolean> fillyCam = PonyConfig.getInstance().fillycam;
fillyCam.set(false);
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 {
@Override
public void processImage(NativeImage image, boolean legacy) {
if (legacy) {

View file

@ -1,12 +1,11 @@
package com.minelittlepony.client;
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.pony.PonyManager;
import com.minelittlepony.client.pony.VariatedTextureSupplier;
import com.minelittlepony.client.model.armour.ArmourTextureResolver;
import com.minelittlepony.client.render.MobRenderers;
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.element.Button;
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.util.GamePaths;
import java.nio.file.Path;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
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.option.KeyBinding;
import net.minecraft.client.util.InputUtil;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager;
@ -40,14 +42,13 @@ public class MineLittlePony implements ClientModInitializer {
public static final Logger logger = LogManager.getLogger("MineLittlePony");
private final PonyRenderDispatcher renderManager = PonyRenderDispatcher.getInstance();
private ClientPonyConfig config;
private PonyManager ponyManager;
private PonyManagerImpl ponyManager;
private VariatedTextureSupplier variatedTextures;
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 hasModMenu;
@ -67,8 +68,8 @@ public class MineLittlePony implements ClientModInitializer {
hasHdSkins = FabricLoader.getInstance().isModLoaded("hdskins");
hasModMenu = FabricLoader.getInstance().isModLoaded("modmenu");
config = new ClientPonyConfig(GamePaths.getConfigDirectory().resolve("minelp.json"));
ponyManager = new PonyManager(config);
PonyConfig config = new ClientPonyConfig(GamePaths.getConfigDirectory().resolve("minelp.json"));
ponyManager = new PonyManagerImpl(config);
variatedTextures = new VariatedTextureSupplier();
KeyBindingHelper.registerKeyBinding(keyBinding);
@ -82,7 +83,9 @@ public class MineLittlePony implements ClientModInitializer {
// general events
ClientReadyCallback.Handler.register();
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);
config.load();
@ -93,10 +96,6 @@ public class MineLittlePony implements ClientModInitializer {
FabricLoader.getInstance().getEntrypoints("minelittlepony", ClientModInitializer.class).forEach(ClientModInitializer::onInitializeClient);
}
private void onClientReady(MinecraftClient client) {
renderManager.initialise(client.getEntityRenderDispatcher());
}
private void onTick(MinecraftClient client) {
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()) {
client.setScreen(new GuiPonySettings(client.currentScreen));
client.setScreen(new PonySettingsScreen(client.currentScreen));
}
}
private void onScreenInit(Screen screen, ScreenInitCallback.ButtonList buttons) {
if (screen instanceof TitleScreen) {
VisibilityMode mode = config.horseButton.get();
VisibilityMode mode = ClientPonyConfig.getInstance().horseButton.get();
boolean show = mode == VisibilityMode.ON || (mode == VisibilityMode.AUTO
&& !(hasHdSkins || hasModMenu
));
@ -121,7 +120,7 @@ public class MineLittlePony implements ClientModInitializer {
if (show) {
int y = hasHdSkins ? 75 : 50;
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()
.setIcon(new TextureSprite()
.setPosition(2, 2)
@ -134,19 +133,36 @@ public class MineLittlePony implements ClientModInitializer {
}
}
/**
* Gets the global MineLP client configuration.
*/
public PonyConfig getConfig() {
return config;
}
public PonyManager getManager() {
public PonyManagerImpl getManager() {
return ponyManager;
}
public VariatedTextureSupplier getVariatedTextures() {
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;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.api.pony.PonyPosture;
import com.minelittlepony.client.transform.PonyTransformation;
@ -16,8 +16,8 @@ public class PonyBounds {
return PonyPosture.getMountPony(entity).map(ridingPony -> {
LivingEntity vehicle = (LivingEntity)entity.getVehicle();
Vec3d offset = PonyTransformation.forSize(ridingPony.metadata().getSize()).getRiderOffset();
float scale = ridingPony.metadata().getSize().getScaleFactor();
Vec3d offset = PonyTransformation.forSize(ridingPony.size()).getRiderOffset();
float scale = ridingPony.metadata().size().scaleFactor();
return getAbsoluteRidingOffset(vehicle).add(
0,
@ -31,7 +31,7 @@ public class PonyBounds {
float delta = MinecraftClient.getInstance().getTickDelta();
Entity vehicle = entity.getVehicle();
double vehicleOffset = vehicle == null ? 0 : vehicle.getHeight() - vehicle.getMountedHeightOffset();
double vehicleOffset = vehicle == null ? 0 : vehicle.getHeight();
return new Vec3d(
MathHelper.lerp(delta, entity.prevX, entity.getX()),
@ -40,8 +40,8 @@ public class PonyBounds {
);
}
public static Box getBoundingBox(IPony pony, LivingEntity entity) {
final float scale = pony.metadata().getSize().getScaleFactor() + 0.1F;
public static Box getBoundingBox(Pony pony, LivingEntity entity) {
final float scale = pony.size().scaleFactor() + 0.1F;
final float width = entity.getWidth() * scale;
final float height = entity.getHeight() * scale;

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;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.*;
import com.minelittlepony.api.config.PonyConfig;
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.ScrollContainer;
import com.minelittlepony.common.client.gui.Tooltip;
@ -23,7 +24,7 @@ import org.jetbrains.annotations.Nullable;
* 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 PONY_LEVEL = OPTIONS_PREFIX + "ponylevel";
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_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 boolean hiddenOptions;
public GuiPonySettings(@Nullable Screen parent) {
public PonySettingsScreen(@Nullable Screen parent) {
super(Text.literal(OPTIONS_PREFIX + "title"), parent);
config = (ClientPonyConfig)MineLittlePony.getInstance().getConfig();
content.margin.top = 30;
content.margin.bottom = 30;
content.getContentPadding().top = 10;
@ -113,7 +119,12 @@ public class GuiPonySettings extends GameGui {
boolean enabled = i != config.fillycam || allowCameraChange;
Button button = content
.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);
button.getStyle().setText(OPTIONS_PREFIX + i.name().toLowerCase());
if (!enabled) {
@ -153,7 +164,7 @@ public class GuiPonySettings extends GameGui {
row += 15;
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) {
@ -182,14 +193,17 @@ public class GuiPonySettings extends GameGui {
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float partialTicks) {
renderBackground(matrices);
super.render(matrices, mouseX, mouseY, partialTicks);
content.render(matrices, mouseX, mouseY, partialTicks);
public void render(DrawContext context, int mouseX, int mouseY, float tickDelta) {
super.render(context, mouseX, mouseY, tickDelta);
content.render(context, mouseX, mouseY, tickDelta);
}
@Override
public void removed() {
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.minecraft.client.MinecraftClient;
@ -6,13 +6,14 @@ import net.minecraft.entity.Entity;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.util.MathUtil;
import java.util.*;
public class VariatedTextureSupplier implements SimpleSynchronousResourceReloadListener {
private static final Identifier ID = new Identifier("minelittlepony", "variated_textures");
public static final Identifier BACKGROUND_PONIES_POOL = new Identifier("minelittlepony", "textures/entity/pony");
public static final Identifier BACKGROUND_ZOMPONIES_POOL = new Identifier("minelittlepony", "textures/entity/zompony");
private 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.pony.IPonyData;
import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.config.PonyLevel;
import com.minelittlepony.api.pony.*;
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.element.Button;
import com.minelittlepony.common.event.ClientReadyCallback;
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.player.DummyPlayer;
import com.minelittlepony.hdskins.client.gui.player.skins.PlayerSkins.PlayerSkin;
import com.minelittlepony.hdskins.client.profile.SkinLoader.ProvidedSkins;
import com.minelittlepony.hdskins.profile.SkinType;
import com.mojang.authlib.GameProfile;
@ -29,8 +28,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import com.minelittlepony.client.pony.PonyManager;
import com.minelittlepony.client.render.entity.PlayerSeaponyRenderer;
import com.minelittlepony.client.*;
/**
* All the interactions with HD Skins.
@ -38,15 +36,18 @@ import com.minelittlepony.client.render.entity.PlayerSeaponyRenderer;
public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer {
static SkinType seaponySkinType;
static SkinType nirikSkinType;
static final Map<SkinType, Wearable> wearableTypes = new HashMap<>();
@Override
public void onInitializeClient() {
SkinsProxy.instance = this;
PonySettingsScreen.buttonFactory = this::renderOption;
seaponySkinType = SkinType.register(PlayerSeaponyRenderer.SKIN_TYPE_ID, Items.COD_BUCKET.getDefaultStack());
Wearable.VALUES.forEach(wearable -> {
seaponySkinType = SkinType.register(DefaultPonySkinHelper.SEAPONY_SKIN_TYPE_ID, Items.COD_BUCKET.getDefaultStack());
nirikSkinType = SkinType.register(DefaultPonySkinHelper.NIRIK_SKIN_TYPE_ID, Items.LAVA_BUCKET.getDefaultStack());
Wearable.REGISTRY.values().forEach(wearable -> {
if (wearable != Wearable.NONE) {
wearableTypes.put(SkinType.register(wearable.getId(), Items.BUNDLE.getDefaultStack()), wearable);
}
@ -54,16 +55,34 @@ public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer {
ClientReadyCallback.EVENT.register(client -> {
// Clear ponies when skins are cleared
PonyManager ponyManager = (PonyManager) MineLittlePony.getInstance().getManager();
SkinCacheClearCallback.EVENT.register(ponyManager::clearCache);
SkinCacheClearCallback.EVENT.register(MineLittlePony.getInstance().getManager()::clearCache);
// Ponify the skins GUI.
GuiSkins.setSkinsGui(GuiSkinsMineLP::new);
});
HDSkins.getInstance().getSkinPrioritySorter().addSelector((skinType, playerSkins) -> {
if (skinType == SkinType.SKIN && PonyConfig.getInstance().mixedHumanSkins.get()) {
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
public void renderOption(Screen screen, @Nullable Screen parent, int row, int RIGHT, ScrollContainer content) {
static Optional<Pony> getPony(PlayerSkinLayers.Layer layer) {
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))
.onClick(button -> MinecraftClient.getInstance().setScreen(
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) {
PlayerSkins skins = PlayerSkins.of(player);
if (skins != null) {
return skins.getProvidedSkinTypes();
}
return PlayerSkins.of(player)
.map(PlayerSkins::layers)
.map(PlayerSkinLayers::combined)
.map(PlayerSkinLayers.Layer::getProvidedSkinTypes)
.orElseGet(Set::of);
}
return Set.of();
@ -103,10 +123,10 @@ public class MineLPHDSkins extends SkinsProxy implements ClientModInitializer {
PlayerSkin main = dummy.getTextures().get(SkinType.SKIN);
Wearable wearable = Wearable.REGISTRY.getOrDefault(type.getId(), Wearable.NONE);
IPonyData metadata = IPony.getManager().getPony(main.getId()).metadata();
if (wearable != Wearable.NONE && metadata.isWearing(wearable)) {
PonyData metadata = Pony.getManager().getPony(main.getId()).metadata();
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());
}
@ -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
public Identifier getSkinTexture(GameProfile profile) {
Identifier skin = HDSkins.getInstance().getProfileRepository().getTextures(profile).get(SkinType.SKIN);
if (skin != null) {
return skin;
}
return super.getSkinTexture(profile);
return HDSkins.getInstance().getProfileRepository()
.load(profile)
.getNow(ProvidedSkins.EMPTY)
.getSkin(SkinType.SKIN)
.orElseGet(() -> 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.ModMenuApi;
import com.minelittlepony.client.GuiPonySettings;
import com.minelittlepony.client.PonySettingsScreen;
public class MineLPModMenuFactory implements ModMenuApi {
@Override
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;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityDimensions;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Entity.class)
public interface IResizeable {
@Accessor("dimensions")
EntityDimensions getCurrentSize();
@Accessor("dimensions")
void setCurrentSize(EntityDimensions size);
@Accessor
void setStandingEyeHeight(float height);
}

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

View file

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

View file

@ -1,12 +1,13 @@
package com.minelittlepony.client.mixin;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.config.PonyLevel;
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.SkinTextures;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@ -20,29 +21,17 @@ abstract class MixinDefaultSkinHelper {
at = @At("RETURN"),
cancellable = true)
private static void onGetTexture(CallbackInfoReturnable<Identifier> cir) {
if (MineLittlePony.getInstance().getConfig().ponyLevel.get() == PonyLevel.PONIES) {
cir.setReturnValue(DefaultPonySkinHelper.getPonySkin(cir.getReturnValue()));
if (PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES) {
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"),
cancellable = true)
private static void onGetTexture(UUID uuid, CallbackInfoReturnable<Identifier> cir) {
if (MineLittlePony.getInstance().getConfig().ponyLevel.get() == PonyLevel.PONIES) {
cir.setReturnValue(DefaultPonySkinHelper.getPonySkin(cir.getReturnValue()));
}
}
@Inject(method = "getModel(Ljava/util/UUID;)Ljava/lang/String;",
at = @At("RETURN"),
cancellable = true)
private static void onGetModel(UUID uuid, CallbackInfoReturnable<String> cir) {
if (MineLittlePony.getInstance().getConfig().ponyLevel.get() == PonyLevel.PONIES) {
cir.setReturnValue(IPony.getManager()
.getPony(DefaultSkinHelper.getTexture(uuid), uuid)
.race()
.getModelId("slim".equalsIgnoreCase(cir.getReturnValue())));
private static void onGetTexture(UUID uuid, CallbackInfoReturnable<SkinTextures> cir) {
if (PonyConfig.getInstance().ponyLevel.get() == PonyLevel.PONIES) {
cir.setReturnValue(DefaultPonySkinHelper.getTextures(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.Redirect;
import com.minelittlepony.client.render.PonyRenderDispatcher;
import com.minelittlepony.client.MineLittlePony;
import org.jetbrains.annotations.Nullable;
@ -42,6 +42,6 @@ abstract class MixinHeldItemRenderer {
VertexConsumerProvider renderContext,
@Nullable World world,
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;
import com.minelittlepony.api.model.*;
import com.minelittlepony.api.model.fabric.PonyModelPrepareCallback;
import com.minelittlepony.api.pony.meta.Sizes;
import com.minelittlepony.api.events.PonyModelPrepareCallback;
import com.minelittlepony.api.pony.meta.SizePreset;
import com.minelittlepony.client.transform.PonyTransformation;
import com.minelittlepony.client.util.render.RenderList;
import com.minelittlepony.util.MathUtil;
import com.minelittlepony.util.MathUtil.Angles;
import java.util.ArrayList;
import java.util.List;
@ -15,7 +16,7 @@ import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
/**
@ -49,7 +50,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
protected final RenderList mainRenderList;
private final List<IPart> parts = new ArrayList<>();
private final List<SubModel> parts = new ArrayList<>();
public AbstractPonyModel(ModelPart tree) {
super(tree);
@ -65,18 +66,18 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
.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);
return part;
}
protected RenderList forPart(Supplier<IPart> part) {
protected RenderList forPart(Supplier<SubModel> part) {
return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
part.get().renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes);
};
}
protected RenderList forPart(IPart part) {
protected RenderList forPart(SubModel part) {
return (stack, vertices, overlayUv, lightUv, red, green, blue, alpha) -> {
part.renderPart(stack, vertices, overlayUv, lightUv, red, green, blue, alpha, attributes);
};
@ -101,11 +102,11 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
*/
@Override
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);
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);
@ -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) {
float pitch = (float)Math.toRadians(attributes.motionPitch);
float pitch = attributes.motionPitch * MathHelper.RADIANS_PER_DEGREE;
head.setAngles(
MathHelper.clamp(attributes.isSleeping ? 0.1f : headPitch / 57.29578F, -1.25f - pitch, 0.5f - pitch),
attributes.isSleeping ? (Math.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
);
@ -148,10 +148,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
} else {
adjustBody(0, ORIGIN);
rightLeg.pivotY = FRONT_LEGS_Y;
leftLeg.pivotY = FRONT_LEGS_Y;
if (!attributes.isSleeping) {
if (!attributes.isLyingDown) {
animateBreathing(animationProgress);
}
@ -161,11 +158,17 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
}
}
if (attributes.isSleeping) {
if (attributes.isLyingDown) {
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) {
@ -183,9 +186,6 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
rightArm.pitch -= LEG_SNEAKING_PITCH_ADJUSTMENT;
leftArm.pitch -= LEG_SNEAKING_PITCH_ADJUSTMENT;
leftLeg.pivotY = FRONT_LEGS_Y;
rightLeg.pivotY = FRONT_LEGS_Y;
}
protected void ponySleep() {
@ -196,7 +196,6 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
leftLeg.pitch = MathUtil.Angles._90_DEG;
HEAD_SLEEPING.set(head);
head.pivotZ = sneaking ? -1 : 1;
FONT_LEGS_SLEEPING.add(rightArm);
FONT_LEGS_SLEEPING.add(leftArm);
@ -275,6 +274,9 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
leftArm.pivotZ = 2 - sin;
float legRPX = attributes.getMainInterpolator().interpolate("legOffset", cos - getLegOutset() - 0.001F, 2);
if (attributes.isHorsey) {
legRPX += 2;
}
rightArm.pivotX = -legRPX;
rightLeg.pivotX = -legRPX;
@ -285,8 +287,12 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
rightArm.yaw += body.yaw;
leftArm.yaw += body.yaw;
rightArm.pivotY = leftArm.pivotY = 8;
rightLeg.pivotZ = leftLeg.pivotZ = 11;
if (attributes.isHorsey) {
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() {
if (attributes.isSleeping) {
if (attributes.isLyingDown) {
return 3.6f;
}
@ -360,6 +366,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
case NECK: return neck;
case TAIL:
case LEGS:
case BACK:
case BODY: return body;
}
}
@ -380,13 +387,14 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
if (attributes.shouldLiftArm(pose, complement, sigma)) {
float swag = 1;
if (!isFlying() && both) {
if (!getAttributes().isFlying && both) {
swag -= (float)Math.pow(limbSpeed, 2);
}
float mult = 1 - swag/2;
arm.pitch = arm.pitch * mult - (MathHelper.PI / 10) * swag;
arm.roll = -sigma * (MathHelper.PI / 15);
arm.roll += 0.3F * -limbSpeed * sigma;
if (attributes.isCrouching) {
arm.pivotX -= sigma * 2;
@ -400,6 +408,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
case BLOCK:
arm.pitch = (arm.pitch / 2 - 0.9424779F) - 0.3F;
arm.yaw = sigma * MathHelper.PI / 9;
arm.roll += 0.3F * -limbSpeed * sigma;
if (complement == pose) {
arm.yaw -= sigma * MathHelper.PI / 18;
}
@ -423,9 +432,12 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
arm.pitch = -0.8F;
arm.yaw = head.yaw + 0.06F;
arm.roll += 0.3F * -limbSpeed * sigma;
break;
case THROW_SPEAR:
arm.pitch = MathUtil.Angles._90_DEG * 2;
arm.roll += (0.3F * -limbSpeed + 0.6F) * sigma;
arm.pivotY ++;
break;
case SPYGLASS:
float addedPitch = sneaking ? -0.2617994F : 0;
@ -438,13 +450,24 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
arm.pivotX -= 6 * sigma;
arm.pivotZ -= 2;
}
if (getSize() == Sizes.TALL) {
if (getSize() == SizePreset.TALL) {
arm.pivotY += 1;
}
if (getSize() == Sizes.FOAL) {
if (getSize() == SizePreset.FOAL) {
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;
default:
break;
@ -467,7 +490,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
* @param entity The entity we are being called for.
*/
protected void swingItem(T entity) {
if (getSwingAmount() > 0 && !attributes.isSleeping) {
if (getSwingAmount() > 0 && !attributes.isLyingDown) {
Arm mainSide = getPreferredArm(entity);
swingArm(getArm(mainSide));
@ -517,7 +540,14 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
protected void adjustBody(float pitch, Pivot pivot) {
adjustBodyComponents(pitch, pivot);
if (!attributes.isHorsey) {
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) {
@ -528,7 +558,7 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
@Override
public float getRiderYOffset() {
switch ((Sizes)getSize()) {
switch ((SizePreset)getSize()) {
case NORMAL: return 0.4F;
case FOAL:
case TALL:
@ -541,16 +571,77 @@ public abstract class AbstractPonyModel<T extends LivingEntity> extends ClientPo
public void setVisible(boolean visible) {
super.setVisible(visible);
neck.visible = visible;
hat.visible &= !attributes.isHorsey;
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
public void transform(BodyPart part, MatrixStack stack) {
if (attributes.isHorsey) {
stack.translate(0, 0.1F, 0);
}
if (attributes.isSleeping || attributes.isRiptide) {
stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180));
}
boolean crouching = attributes.isCrouching;
if (attributes.isLyingDown && !attributes.isSleeping) {
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);
attributes.isCrouching = crouching;
}
}

View file

@ -8,12 +8,12 @@ import net.minecraft.util.Hand;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.model.fabric.PonyModelPrepareCallback;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.api.events.PonyModelPrepareCallback;
import com.minelittlepony.api.model.*;
import com.minelittlepony.api.pony.Pony;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.api.pony.meta.Size;
import com.minelittlepony.api.pony.meta.Sizes;
import com.minelittlepony.api.pony.meta.SizePreset;
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.
*/
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.
@ -47,11 +47,11 @@ public abstract class ClientPonyModel<T extends LivingEntity> extends MsonPlayer
}
@Override
public void updateLivingState(T entity, IPony pony, ModelAttributes.Mode mode) {
public void updateLivingState(T entity, Pony pony, ModelAttributes.Mode mode) {
child = entity.isBaby();
attributes.updateLivingState(entity, pony, mode);
PonyModelPrepareCallback.EVENT.invoker().onPonyModelPrepared(entity, this, mode);
sneaking = attributes.isCrouching;
sneaking = attributes.isCrouching && !attributes.isLyingDown;
riding = attributes.isSitting;
}
@ -60,35 +60,6 @@ public abstract class ClientPonyModel<T extends LivingEntity> extends MsonPlayer
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.
*/
@ -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
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> {
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.util.Identifier;
import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.model.gear.IGear;
import com.minelittlepony.api.model.BodyPart;
import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.api.model.gear.*;
import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.client.model.entity.*;
import com.minelittlepony.client.model.entity.race.*;
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.Mson;
import com.minelittlepony.mson.api.MsonModel;
@ -31,7 +30,7 @@ import java.util.stream.Stream;
public final class ModelType {
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);
@ -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<?>> 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_LEFT = registerGear(SADDLEBAGS_BOTH, Wearable.SADDLE_BAGS_LEFT, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_LEFT));
public static final GearModelKey<SaddleBags> SADDLEBAGS_RIGHT = registerGear(SADDLEBAGS_BOTH, Wearable.SADDLE_BAGS_RIGHT, t -> new SaddleBags(t, Wearable.SADDLE_BAGS_RIGHT));
public static final GearModelKey<Crown> CROWN = registerGear("crown", Wearable.CROWN, Crown::new);
public static final GearModelKey<Muffin> MUFFIN = registerGear("muffin", Wearable.MUFFIN, Muffin::new);
public static final GearModelKey<WitchHat> WITCH_HAT = registerGear("witch_hat", Wearable.HAT, WitchHat::new);
public static final GearModelKey<ChristmasHat> ANTLERS = registerGear("antlers", Wearable.ANTLERS, ChristmasHat::new);
public static final GearModelKey<AbstractGearModel> MUFFIN = registerGear("muffin", Wearable.MUFFIN, t -> new WearableGear(Wearable.MUFFIN, BodyPart.HEAD, 0.45F).addPart(t.getChild("crown")));
public static final GearModelKey<AbstractGearModel> WITCH_HAT = registerGear("witch_hat", Wearable.HAT, t -> new WearableGear(Wearable.HAT, BodyPart.HEAD, 0.7F).addPart(t.getChild("hat")));
public static final GearModelKey<DeerAntlers> ANTLERS = registerGear("antlers", Wearable.ANTLERS, DeerAntlers::new);
public static final PlayerModelKey<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<?>> 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<?>> 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, 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, ChangelingModel<?>> CHANGELING = registerPlayer("changeling", Race.CHANGELING, ChangelingModel::new);
public static final PlayerModelKey<LivingEntity, ChangelingModel<?>> CHANGEDLING = registerPlayer("reformed_changeling", Race.CHANGEDLING, ChangelingModel::new);
public static final PlayerModelKey<LivingEntity, EarthPonyModel<?>> ZEBRA = registerPlayer("zebra", Race.ZEBRA, EarthPonyModel::new);
static <E extends LivingEntity, T extends Model & MsonModel & 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) {
return registerPlayer(name, race, constructor, PlayerPonyRenderer::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);
return registerPlayer(name, race, constructor, PonyArmourModel::new);
}
@SuppressWarnings("unchecked")
static <E extends LivingEntity, T extends Model & MsonModel & IModel> PlayerModelKey<E, T> registerPlayer(String name, Race race,
static <E extends LivingEntity, T extends Model & MsonModel & PonyModel<?>> PlayerModelKey<E, T> registerPlayer(String name, Race race,
BiFunction<ModelPart, Boolean, T> constructor,
MsonModel.Factory<PonyArmourModel<E>> armorFactory,
PlayerModelKey.RendererFactory rendererFactory) {
return (PlayerModelKey<E, T>)PLAYER_MODELS.computeIfAbsent(race, r -> {
return new PlayerModelKey<>(name, constructor, rendererFactory, armorFactory);
});
MsonModel.Factory<PonyArmourModel<E>> armorFactory) {
return (PlayerModelKey<E, T>)PLAYER_MODELS.computeIfAbsent(race, r -> new PlayerModelKey<>(name, constructor, armorFactory));
}
@SuppressWarnings("unchecked")
static <T extends 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 new GearModelKey<T>(Mson.getInstance().registerModel(new Identifier("minelittlepony", "gear/" + name), constructor), constructor);
});
}
@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));
}
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")
@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);
}
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();
}
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() {
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.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.util.Identifier;
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.mson.api.ModelKey;
import com.minelittlepony.mson.api.Mson;
import com.minelittlepony.mson.api.MsonModel;
import com.minelittlepony.mson.api.*;
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> alexKey,
RendererFactory factory,
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(
Mson.getInstance().registerModel(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)),
rendererFactory,
new ModelKeyImpl<>(new Identifier("minelittlepony", "races/steve/" + name), tree -> modelFactory.apply(tree, false)),
new ModelKeyImpl<>(new Identifier("minelittlepony", "races/alex/" + name), tree -> modelFactory.apply(tree, true)),
armorFactory
);
}
@ -37,25 +31,12 @@ public record PlayerModelKey<T extends LivingEntity, M extends Model & MsonModel
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);
}
@SuppressWarnings({"rawtypes", "unchecked"})
public <K extends T, N extends M> ModelWrapper<K, N> create(boolean slimArms, @Nullable Consumer<N> initializer) {
return new ModelWrapper(this, slimArms, initializer);
}
@SuppressWarnings("unchecked")
public Function<EntityRendererFactory.Context, PlayerEntityRenderer> getFactory(boolean slimArms) {
return d -> factory.create(d, slimArms, (PlayerModelKey<AbstractClientPlayerEntity, ClientPonyModel<AbstractClientPlayerEntity>>)this);
}
public interface RendererFactory {
PlayerEntityRenderer create(
EntityRendererFactory.Context context,
boolean slim,
PlayerModelKey<AbstractClientPlayerEntity, ClientPonyModel<AbstractClientPlayerEntity>> key
);
public <E extends T, N extends M> Models<E, N> create(boolean slimArms, @Nullable Consumer<N> initializer) {
return new Models(this, slimArms, initializer);
}
}

View file

@ -1,10 +1,9 @@
package com.minelittlepony.api.model.armour;
package com.minelittlepony.client.model.armour;
import net.minecraft.item.Item;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;
import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.mson.api.ModelKey;
import com.minelittlepony.mson.api.Mson;
@ -18,7 +17,7 @@ public interface ArmorModelRegistry {
if (id.getNamespace().equals("minecraft")) {
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));
}).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.

View file

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

View file

@ -6,6 +6,8 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.mob.EndermanEntity;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.api.pony.meta.Race;
public class EnderStallionModel extends SkeleponyModel<EndermanEntity> {
public boolean isCarrying;
@ -54,8 +56,8 @@ public class EnderStallionModel extends SkeleponyModel<EndermanEntity> {
}
@Override
public boolean canFly() {
return isAlicorn;
public Race getRace() {
return isAlicorn ? (super.getRace().hasHorn() ? Race.ALICORN : Race.PEGASUS) : super.getRace();
}
@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.entity.mob.GuardianEntity;
import com.minelittlepony.client.model.IPonyMixinModel;
import com.minelittlepony.api.model.PonyModelMixin;
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;
public GuardianPonyModel(ModelPart tree) {
@ -18,28 +18,29 @@ public class GuardianPonyModel extends GuardianEntityModel implements IPonyMixin
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
public SeaponyModel<GuardianEntity> mixin() {
return mixin;
}
@Override
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) {
mixin().render(matrices, vertices, light, overlay, red, green, blue, alpha);
}
@Override
public void animateModel(GuardianEntity entity, float limbAngle, float limbDistance, float tickDelta) {
mixin().animateModel(entity, limbAngle, limbDistance, tickDelta);
}
@Override
public void copyStateTo(EntityModel<GuardianEntity> copy) {
mixin().copyStateTo(copy);
}
@Override
public void setAngles(GuardianEntity entity, float limbAngle, float limbSpeed, float animationProgress, float headYaw, float headPitch) {
mixin().setVisible(true);
mixin().setAngles(entity, limbAngle, limbSpeed, animationProgress, headYaw, headPitch);
}
}

View file

@ -7,7 +7,7 @@ import net.minecraft.entity.mob.PiglinActivity;
import net.minecraft.util.math.MathHelper;
import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.Pony;
public class PiglinPonyModel extends ZomponyModel<HostileEntity> {
@ -23,7 +23,7 @@ public class PiglinPonyModel extends ZomponyModel<HostileEntity> {
}
@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);
leftArmPose = ArmPose.EMPTY;
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.Hand;
import com.minelittlepony.client.model.IMobModel;
import com.minelittlepony.api.model.MobPosingHelper;
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;
@ -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) {
IMobModel.rotateArmHolding(arm, direction, swingProgress, ticks);
MobPosingHelper.rotateArmHolding(arm, direction, swingProgress, ticks);
}
@Override
@ -78,7 +78,7 @@ public class SkeleponyModel<T extends HostileEntity> extends AlicornModel<T> imp
@Override
protected float getLegOutset() {
if (attributes.isSleeping) return 2.6f;
if (attributes.isLyingDown) return 2.6f;
if (attributes.isCrouching) return 0;
return 4;
}

View file

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

View file

@ -1,11 +1,12 @@
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 net.minecraft.client.model.ModelPart;
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;
@ -23,13 +24,13 @@ public class ZomponyModel<Zombie extends HostileEntity> extends AlicornModel<Zom
protected void rotateLegs(float move, float swing, float ticks, Zombie entity) {
super.rotateLegs(move, swing, ticks, entity);
if (isZombified(entity)) {
IMobModel.rotateUndeadArms(this, move, ticks);
MobPosingHelper.rotateUndeadArms(this, move, ticks);
}
}
@Override
public boolean canFly() {
return isPegasus;
public Race getRace() {
return isPegasus ? (super.getRace().hasHorn() ? Race.ALICORN : Race.PEGASUS) : super.getRace();
}
protected boolean isZombified(Zombie entity) {

View file

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

View file

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

View file

@ -1,6 +1,6 @@
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.part.*;
import com.minelittlepony.mson.api.ModelView;
@ -12,12 +12,19 @@ public class EarthPonyModel<T extends LivingEntity> extends AbstractPonyModel<T>
private final boolean smallArms;
protected IPart tail;
protected SubModel tail;
protected PonySnout snout;
protected PonyEars ears;
private final ModelPart mane;
private final ModelPart nose;
private final ModelPart tailStub;
public EarthPonyModel(ModelPart tree, boolean smallArms) {
super(tree);
mane = neck.getChild("mane");
nose = head.getChild("nose");
tailStub = body.getChild("tail_stub");
this.smallArms = smallArms;
}
@ -45,4 +52,12 @@ public class EarthPonyModel<T extends LivingEntity> extends AbstractPonyModel<T>
}
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;
import com.minelittlepony.api.model.IPart;
import com.minelittlepony.api.model.IPegasus;
import com.minelittlepony.api.model.SubModel;
import com.minelittlepony.api.model.WingedPonyModel;
import com.minelittlepony.client.model.part.PonyWings;
import com.minelittlepony.mson.api.ModelView;
import net.minecraft.client.model.ModelPart;
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;
@ -24,7 +24,7 @@ public class PegasusModel<T extends LivingEntity> extends EarthPonyModel<T> impl
}
@Override
public IPart getWings() {
public SubModel getWings() {
return wings;
}
}

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