mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-30 16:28:00 +01:00
Merge branch '1.20' into 1.20.2
This commit is contained in:
commit
0ab31bf3ac
666 changed files with 12499 additions and 1781 deletions
|
@ -72,7 +72,7 @@
|
|||
|
||||
- Захват бурь
|
||||
|
||||
Дайте пегасу облако, и у него будет вода на день, дайте пегасу банку, и он поймает облака и устроит дождь на всё лето.
|
||||
Дайте пегасу облако, и у него будет вода на день, дайте пегасу банку, и он поймает облако и устроит дождь на всё лето.
|
||||
Кажется, так гласит поговорка? Так вот, с помощью банок можно собрать дождь в банку во время грозы.
|
||||
|
||||
Это позволит и остановить дождь, и сохранить погоду в банке для последующего использования.
|
||||
|
|
1
assets/models/block_shell.bbmodel
Normal file
1
assets/models/block_shell.bbmodel
Normal file
|
@ -0,0 +1 @@
|
|||
{"meta":{"format_version":"4.5","model_format":"java_block","box_uv":false},"name":"block_shell","parent":"","ambientocclusion":true,"front_gui_light":false,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":16,"height":16},"elements":[{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2.4000000000000004,0,0],"to":[10.399999999999999,0,8],"autouv":0,"color":9,"rotation":[0,45,0],"origin":[12,0,4],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"texture":0},"down":{"uv":[0,0,0,0],"texture":null}},"type":"cube","uuid":"1c53fe40-1877-60c2-80b2-135dff0afe56"}],"outliner":["1c53fe40-1877-60c2-80b2-135dff0afe56"],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/src/main/resources/assets/unicopia/textures/item/clam_shell.png","name":"clam_shell.png","folder":"item","namespace":"unicopia","id":"shell","particle":true,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"1a1accdc-ffd7-c20a-9047-a5db72ecf496","relative_path":"../../../src/main/resources/assets/unicopia/textures/item/clam_shell.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAihJREFUOE+l09tL02Ecx/H3+C3XDr9y+21Yk9lhbWyyxYg8BFoXkXRYQlQEi6CCLosg6H+IoIu6CIoIDaKgjPJGwhBUQqVINHYo+0XqXB6mzsPSxm9PYLIQMwifywc+L57vh+erY51Ht848/wQaW9+LzFSaYrsDq8OBGo9y5VTdisxfgdtPW4Sy1UltbZBFQAJsQErT+BJXCQe8hdwq4O6LNrGnJkCp3U5/NFqYsAxILCwwPZ7j4uHqtYGXvb3CqGlYDAasPh8+SUJdZjpa33H+UMXaIzxq+yC8pp/YKyuxAKP9/ej1ejKZDB0D37l+7sSqF6+4eNYTFaEKPzpVJZ/P8zlnIZfLUb/bxafhEVre9uELedA0E0fLnUvZAvCks084t5WhzCRxuVykZBnt8iv8d+oLPWRuqLDRTKOzj9Id2zlZ4dUtATcfNgnbJoHX70dSdrHvwSBjCAzGzeApQj4ik23KoqkTsDiHGs4RzxYRqQksAw3PhVJiR7bacClGJIsFz60EmlFGMphB2gCzEwydMeJ2u3kTizEzJ3H2QOg30PC6RyhbrKRGktgUJwf3eoh3dzNtMuExm5mfnycYDNLV1cXO6mraexKcrvL96eBec7tIqgNYHSVcjRzTPe78KIptNjxuBf3kJD9kmfFEgrJQCPXbLIMDCS7UVa0s8X5zu7h0fH+h1O60EF9jUdyBckaHU4yNDIHIM5VOcy0SXvsj/e9yrXsbfwHRlcQRuWinHQAAAABJRU5ErkJggg=="}]}
|
1
assets/models/block_shell_2.bbmodel
Normal file
1
assets/models/block_shell_2.bbmodel
Normal file
|
@ -0,0 +1 @@
|
|||
{"meta":{"format_version":"4.5","model_format":"java_block","box_uv":false},"name":"clam_shell_2","parent":"","ambientocclusion":true,"front_gui_light":false,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":16,"height":16},"elements":[{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[6.4,0,0],"to":[14.399999999999999,0,8],"autouv":0,"color":9,"rotation":[0,45,0],"origin":[12,0,4],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"texture":0},"down":{"uv":[0,0,0,0],"texture":null}},"type":"cube","uuid":"1c53fe40-1877-60c2-80b2-135dff0afe56"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[1.2117749006091447,0,6.788225099390861],"to":[9.211774900609145,0,14.78822509939086],"autouv":0,"color":9,"rotation":[0,-22.5,0],"origin":[5.211774900609143,0,10.788225099390859],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"rotation":90,"texture":0},"down":{"uv":[0,0,0,0],"rotation":270,"texture":null}},"type":"cube","uuid":"f8ee1a59-5c1f-ddd7-783f-bb14a40c36a2"}],"outliner":["1c53fe40-1877-60c2-80b2-135dff0afe56","f8ee1a59-5c1f-ddd7-783f-bb14a40c36a2"],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/src/main/resources/assets/unicopia/textures/item/clam_shell.png","name":"clam_shell.png","folder":"item","namespace":"unicopia","id":"shell","particle":true,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"1a1accdc-ffd7-c20a-9047-a5db72ecf496","relative_path":"../../../src/main/resources/assets/unicopia/textures/item/clam_shell.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAihJREFUOE+l09tL02Ecx/H3+C3XDr9y+21Yk9lhbWyyxYg8BFoXkXRYQlQEi6CCLosg6H+IoIu6CIoIDaKgjPJGwhBUQqVINHYo+0XqXB6mzsPSxm9PYLIQMwifywc+L57vh+erY51Ht848/wQaW9+LzFSaYrsDq8OBGo9y5VTdisxfgdtPW4Sy1UltbZBFQAJsQErT+BJXCQe8hdwq4O6LNrGnJkCp3U5/NFqYsAxILCwwPZ7j4uHqtYGXvb3CqGlYDAasPh8+SUJdZjpa33H+UMXaIzxq+yC8pp/YKyuxAKP9/ej1ejKZDB0D37l+7sSqF6+4eNYTFaEKPzpVJZ/P8zlnIZfLUb/bxafhEVre9uELedA0E0fLnUvZAvCks084t5WhzCRxuVykZBnt8iv8d+oLPWRuqLDRTKOzj9Id2zlZ4dUtATcfNgnbJoHX70dSdrHvwSBjCAzGzeApQj4ik23KoqkTsDiHGs4RzxYRqQksAw3PhVJiR7bacClGJIsFz60EmlFGMphB2gCzEwydMeJ2u3kTizEzJ3H2QOg30PC6RyhbrKRGktgUJwf3eoh3dzNtMuExm5mfnycYDNLV1cXO6mraexKcrvL96eBec7tIqgNYHSVcjRzTPe78KIptNjxuBf3kJD9kmfFEgrJQCPXbLIMDCS7UVa0s8X5zu7h0fH+h1O60EF9jUdyBckaHU4yNDIHIM5VOcy0SXvsj/e9yrXsbfwHRlcQRuWinHQAAAABJRU5ErkJggg=="}]}
|
1
assets/models/changeling.bbmodel
Normal file
1
assets/models/changeling.bbmodel
Normal file
File diff suppressed because one or more lines are too long
1
assets/models/clam_shell_3.bbmodel
Normal file
1
assets/models/clam_shell_3.bbmodel
Normal file
|
@ -0,0 +1 @@
|
|||
{"meta":{"format_version":"4.5","model_format":"java_block","box_uv":false},"name":"clam_shell_3","parent":"","ambientocclusion":true,"front_gui_light":false,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":16,"height":16},"elements":[{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[6.100000000000001,0.4,0.6000000000000014],"to":[14.100000000000005,0.4,8.600000000000001],"autouv":0,"color":9,"rotation":[22.5,0,0],"origin":[10.100000000000005,0.4,3],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"rotation":270,"texture":0},"down":{"uv":[0,0,0,0],"rotation":90,"texture":null}},"type":"cube","uuid":"1c53fe40-1877-60c2-80b2-135dff0afe56"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0.3640158355865708,0,4.022858234660681],"to":[8.36401583558657,0,12.022858234660681],"autouv":0,"color":9,"rotation":[0,0,22.5],"origin":[4.364015835586571,0,8.022858234660681],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"rotation":180,"texture":0},"down":{"uv":[0,0,0,0],"rotation":180,"texture":null}},"type":"cube","uuid":"f8ee1a59-5c1f-ddd7-783f-bb14a40c36a2"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[7.755052095676865,0,8.084325693581398],"to":[15.755052095676865,0,16.0843256935814],"autouv":0,"color":9,"rotation":[-22.5,0,0],"origin":[11.755052095676865,0,12.0843256935814],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"rotation":90,"texture":0},"down":{"uv":[16,0,0,16],"rotation":90,"texture":0}},"type":"cube","uuid":"a13cf19e-6339-91b6-4d9b-0d3500abf734"}],"outliner":["1c53fe40-1877-60c2-80b2-135dff0afe56","f8ee1a59-5c1f-ddd7-783f-bb14a40c36a2","a13cf19e-6339-91b6-4d9b-0d3500abf734"],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/src/main/resources/assets/unicopia/textures/item/clam_shell.png","name":"clam_shell.png","folder":"item","namespace":"unicopia","id":"shell","particle":true,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"1a1accdc-ffd7-c20a-9047-a5db72ecf496","relative_path":"../../../src/main/resources/assets/unicopia/textures/item/clam_shell.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAihJREFUOE+l09tL02Ecx/H3+C3XDr9y+21Yk9lhbWyyxYg8BFoXkXRYQlQEi6CCLosg6H+IoIu6CIoIDaKgjPJGwhBUQqVINHYo+0XqXB6mzsPSxm9PYLIQMwifywc+L57vh+erY51Ht848/wQaW9+LzFSaYrsDq8OBGo9y5VTdisxfgdtPW4Sy1UltbZBFQAJsQErT+BJXCQe8hdwq4O6LNrGnJkCp3U5/NFqYsAxILCwwPZ7j4uHqtYGXvb3CqGlYDAasPh8+SUJdZjpa33H+UMXaIzxq+yC8pp/YKyuxAKP9/ej1ejKZDB0D37l+7sSqF6+4eNYTFaEKPzpVJZ/P8zlnIZfLUb/bxafhEVre9uELedA0E0fLnUvZAvCks084t5WhzCRxuVykZBnt8iv8d+oLPWRuqLDRTKOzj9Id2zlZ4dUtATcfNgnbJoHX70dSdrHvwSBjCAzGzeApQj4ik23KoqkTsDiHGs4RzxYRqQksAw3PhVJiR7bacClGJIsFz60EmlFGMphB2gCzEwydMeJ2u3kTizEzJ3H2QOg30PC6RyhbrKRGktgUJwf3eoh3dzNtMuExm5mfnycYDNLV1cXO6mraexKcrvL96eBec7tIqgNYHSVcjRzTPe78KIptNjxuBf3kJD9kmfFEgrJQCPXbLIMDCS7UVa0s8X5zu7h0fH+h1O60EF9jUdyBckaHU4yNDIHIM5VOcy0SXvsj/e9yrXsbfwHRlcQRuWinHQAAAABJRU5ErkJggg=="}]}
|
1
assets/models/clam_shell_4.bbmodel
Normal file
1
assets/models/clam_shell_4.bbmodel
Normal file
|
@ -0,0 +1 @@
|
|||
{"meta":{"format_version":"4.5","model_format":"java_block","box_uv":false},"name":"clam_shell_4","parent":"","ambientocclusion":true,"front_gui_light":false,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":16,"height":16},"elements":[{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[8,0.5,0.6000000000000014],"to":[16,0.5,8.600000000000001],"autouv":0,"color":9,"rotation":[22.5,0,0],"origin":[12,0.5,3],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"rotation":270,"texture":0},"down":{"uv":[16,0,0,16],"rotation":270,"texture":0}},"type":"cube","uuid":"1c53fe40-1877-60c2-80b2-135dff0afe56"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0.3640158355865708,0,7.022858234660681],"to":[8.36401583558657,0,15.022858234660681],"autouv":0,"color":9,"rotation":[22.5,0,0],"origin":[4.364015835586571,0,11.022858234660681],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"rotation":270,"texture":0},"down":{"uv":[16,0,0,16],"rotation":270,"texture":0}},"type":"cube","uuid":"f8ee1a59-5c1f-ddd7-783f-bb14a40c36a2"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[7.755052095676865,0,8.084325693581398],"to":[15.755052095676865,0,16.0843256935814],"autouv":0,"color":9,"rotation":[-22.5,0,0],"origin":[11.755052095676865,0,12.0843256935814],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"rotation":90,"texture":0},"down":{"uv":[0,16,16,0],"rotation":270,"texture":0}},"type":"cube","uuid":"a13cf19e-6339-91b6-4d9b-0d3500abf734"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[1.3640158355865708,0,0.022858234660681376],"to":[9.36401583558657,0,8.022858234660681],"autouv":0,"color":9,"rotation":[0,0,22.5],"origin":[5.364015835586571,0,4.022858234660681],"faces":{"north":{"uv":[0,0,8,0],"texture":null},"east":{"uv":[0,0,8,0],"texture":null},"south":{"uv":[0,0,8,0],"texture":null},"west":{"uv":[0,0,8,0],"texture":null},"up":{"uv":[0,0,16,16],"rotation":180,"texture":0},"down":{"uv":[0,16,16,0],"rotation":180,"texture":0}},"type":"cube","uuid":"60843fa4-6abd-3b89-20f7-67fc05e29ba3"}],"outliner":["1c53fe40-1877-60c2-80b2-135dff0afe56","f8ee1a59-5c1f-ddd7-783f-bb14a40c36a2","60843fa4-6abd-3b89-20f7-67fc05e29ba3","a13cf19e-6339-91b6-4d9b-0d3500abf734"],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/src/main/resources/assets/unicopia/textures/item/clam_shell.png","name":"clam_shell.png","folder":"item","namespace":"unicopia","id":"shell","particle":true,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"1a1accdc-ffd7-c20a-9047-a5db72ecf496","relative_path":"../../../src/main/resources/assets/unicopia/textures/item/clam_shell.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAihJREFUOE+l09tL02Ecx/H3+C3XDr9y+21Yk9lhbWyyxYg8BFoXkXRYQlQEi6CCLosg6H+IoIu6CIoIDaKgjPJGwhBUQqVINHYo+0XqXB6mzsPSxm9PYLIQMwifywc+L57vh+erY51Ht848/wQaW9+LzFSaYrsDq8OBGo9y5VTdisxfgdtPW4Sy1UltbZBFQAJsQErT+BJXCQe8hdwq4O6LNrGnJkCp3U5/NFqYsAxILCwwPZ7j4uHqtYGXvb3CqGlYDAasPh8+SUJdZjpa33H+UMXaIzxq+yC8pp/YKyuxAKP9/ej1ejKZDB0D37l+7sSqF6+4eNYTFaEKPzpVJZ/P8zlnIZfLUb/bxafhEVre9uELedA0E0fLnUvZAvCks084t5WhzCRxuVykZBnt8iv8d+oLPWRuqLDRTKOzj9Id2zlZ4dUtATcfNgnbJoHX70dSdrHvwSBjCAzGzeApQj4ik23KoqkTsDiHGs4RzxYRqQksAw3PhVJiR7bacClGJIsFz60EmlFGMphB2gCzEwydMeJ2u3kTizEzJ3H2QOg30PC6RyhbrKRGktgUJwf3eoh3dzNtMuExm5mfnycYDNLV1cXO6mraexKcrvL96eBec7tIqgNYHSVcjRzTPe78KIptNjxuBf3kJD9kmfFEgrJQCPXbLIMDCS7UVa0s8X5zu7h0fH+h1O60EF9jUdyBckaHU4yNDIHIM5VOcy0SXvsj/e9yrXsbfwHRlcQRuWinHQAAAABJRU5ErkJggg=="}]}
|
1
assets/models/cloud_chest.bbmodel
Normal file
1
assets/models/cloud_chest.bbmodel
Normal file
File diff suppressed because one or more lines are too long
32
assets/models/cloud_chest.java
Normal file
32
assets/models/cloud_chest.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Made with Blockbench 4.8.3
|
||||
// Exported for Minecraft version 1.17+ for Yarn
|
||||
// Paste this class into your mod and generate all required imports
|
||||
public class cloud_chest extends EntityModel<Entity> {
|
||||
private final ModelPart lid;
|
||||
private final ModelPart lock_r1;
|
||||
private final ModelPart bb_main;
|
||||
public cloud_chest(ModelPart root) {
|
||||
this.lid = root.getChild("lid");
|
||||
this.bb_main = root.getChild("bb_main");
|
||||
}
|
||||
public static TexturedModelData getTexturedModelData() {
|
||||
ModelData modelData = new ModelData();
|
||||
ModelPartData modelPartData = modelData.getRoot();
|
||||
ModelPartData lid = modelPartData.addChild("lid", ModelPartBuilder.create().uv(0, 0).cuboid(-1.0F, -2.0F, 13.8F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
|
||||
.uv(0, 0).cuboid(-1.0F, -1.0F, 14.0F, 2.0F, 2.0F, 1.0F, new Dilation(0.0F))
|
||||
.uv(0, 0).cuboid(-7.0F, -5.0F, 0.0F, 14.0F, 5.0F, 14.0F, new Dilation(0.3F)), ModelTransform.of(0.0F, 16.0F, -7.0F, 1.0908F, 0.0F, 0.0F));
|
||||
|
||||
ModelPartData lock_r1 = lid.addChild("lock_r1", ModelPartBuilder.create().uv(0, 0).cuboid(-2.0F, -4.0F, -0.5F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F)), ModelTransform.of(-2.0F, 1.0F, 14.3F, 0.0F, 0.0F, 1.5708F));
|
||||
|
||||
ModelPartData bb_main = modelPartData.addChild("bb_main", ModelPartBuilder.create().uv(0, 19).cuboid(-7.0F, -10.0F, -7.0F, 14.0F, 10.0F, 14.0F, new Dilation(0.0F)), ModelTransform.pivot(0.0F, 24.0F, 0.0F));
|
||||
return TexturedModelData.of(modelData, 64, 64);
|
||||
}
|
||||
@Override
|
||||
public void setAngles(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) {
|
||||
}
|
||||
@Override
|
||||
public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) {
|
||||
lid.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
|
||||
bb_main.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
|
||||
}
|
||||
}
|
BIN
assets/models/cloud_chest.png
Normal file
BIN
assets/models/cloud_chest.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
15
src/main/java/com/minelittlepony/unicopia/Availability.java
Normal file
15
src/main/java/com/minelittlepony/unicopia/Availability.java
Normal file
|
@ -0,0 +1,15 @@
|
|||
package com.minelittlepony.unicopia;
|
||||
|
||||
public enum Availability {
|
||||
DEFAULT,
|
||||
COMMANDS,
|
||||
NONE;
|
||||
|
||||
public boolean isSelectable() {
|
||||
return this == DEFAULT;
|
||||
}
|
||||
|
||||
public boolean isGrantable() {
|
||||
return this != NONE;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package com.minelittlepony.unicopia;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.entity.mob.AirBalloonEntity;
|
||||
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||
|
||||
import net.minecraft.entity.vehicle.BoatEntity;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface Debug {
|
||||
|
@ -18,6 +20,16 @@ public interface Debug {
|
|||
}
|
||||
TESTS_COMPLETE[0] = true;
|
||||
|
||||
try {
|
||||
Registries.ITEM.getEntrySet().forEach(entry -> {
|
||||
if (SpellTraits.of(entry.getValue()).isEmpty()) {
|
||||
// Unicopia.LOGGER.warn("No traits registered for item {}", entry.getKey());
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
throw new IllegalStateException("Tests failed", t);
|
||||
}
|
||||
|
||||
try {
|
||||
for (var type : BoatEntity.Type.values()) {
|
||||
var balloon = UEntities.AIR_BALLOON.create(world);
|
||||
|
|
|
@ -27,6 +27,7 @@ public interface EquinePredicates {
|
|||
Predicate<Entity> PLAYER_CHANGELING = IS_PLAYER.and(ofRace(Race.CHANGELING));
|
||||
Predicate<Entity> PLAYER_KIRIN = IS_PLAYER.and(ofRace(Race.KIRIN));
|
||||
Predicate<Entity> PLAYER_PEGASUS = IS_PLAYER.and(e -> ((PlayerEntity)e).getAbilities().creativeMode || RACE_INTERACT_WITH_CLOUDS.test(e));
|
||||
Predicate<Entity> PLAYER_SEAPONY = IS_PLAYER.and(raceMatches(Race::isFish));
|
||||
|
||||
Predicate<Entity> PLAYER_CAN_USE_EARTH = IS_PLAYER.and(raceMatches(Race::canUseEarth));
|
||||
Predicate<Entity> IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || IS_PLAYER.test(e));
|
||||
|
|
|
@ -24,37 +24,44 @@ import net.minecraft.util.Identifier;
|
|||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
|
||||
public record Race (Supplier<Composite> compositeSupplier, boolean canCast, FlightType flightType, boolean canUseEarth, boolean isNocturnal, boolean canHang) implements Affine {
|
||||
public record Race (Supplier<Composite> compositeSupplier, Availability availability, boolean canCast, FlightType flightType, boolean canUseEarth, boolean isNocturnal, boolean canHang) implements Affine {
|
||||
public static final String DEFAULT_ID = "unicopia:unset";
|
||||
public static final Registry<Race> REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID);
|
||||
public static final Registry<Race> COMMAND_REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race/grantable"), DEFAULT_ID);
|
||||
public static final RegistryKey<? extends Registry<Race>> REGISTRY_KEY = REGISTRY.getKey();
|
||||
private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("race.unknown", id));
|
||||
|
||||
public static Race register(String name, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) {
|
||||
return register(Unicopia.id(name), magic, flight, earth, nocturnal, canHang);
|
||||
public static Race register(String name, Availability availability, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) {
|
||||
return register(Unicopia.id(name), availability, magic, flight, earth, nocturnal, canHang);
|
||||
}
|
||||
|
||||
public static Race register(Identifier id, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) {
|
||||
return Registry.register(REGISTRY, id, new Race(Suppliers.memoize(() -> new Composite(REGISTRY.get(id), null)), magic, flight, earth, nocturnal, canHang));
|
||||
public static Race register(Identifier id, Availability availability, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) {
|
||||
Race race = Registry.register(REGISTRY, id, new Race(Suppliers.memoize(() -> new Composite(REGISTRY.get(id), null, null)), availability, magic, flight, earth, nocturnal, canHang));
|
||||
if (availability.isGrantable()) {
|
||||
Registry.register(COMMAND_REGISTRY, id, race);
|
||||
}
|
||||
return race;
|
||||
}
|
||||
|
||||
public static RegistryKeyArgumentType<Race> argument() {
|
||||
return RegistryKeyArgumentType.registryKey(REGISTRY_KEY);
|
||||
return RegistryKeyArgumentType.registryKey(COMMAND_REGISTRY.getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* The default, unset race.
|
||||
* This is used if there are no other races.
|
||||
*/
|
||||
public static final Race UNSET = register("unset", false, FlightType.NONE, false, false, false);
|
||||
public static final Race HUMAN = register("human", false, FlightType.NONE, false, false, false);
|
||||
public static final Race EARTH = register("earth", false, FlightType.NONE, true, false, false);
|
||||
public static final Race UNICORN = register("unicorn", true, FlightType.NONE, false, false, false);
|
||||
public static final Race PEGASUS = register("pegasus", false, FlightType.AVIAN, false, false, false);
|
||||
public static final Race BAT = register("bat", false, FlightType.AVIAN, false, true, true);
|
||||
public static final Race ALICORN = register("alicorn", true, FlightType.AVIAN, true, false, false);
|
||||
public static final Race CHANGELING = register("changeling", false, FlightType.INSECTOID, false, false, true);
|
||||
public static final Race KIRIN = register("kirin", true, FlightType.NONE, false, false, false);
|
||||
public static final Race UNSET = register("unset", Availability.COMMANDS, false, FlightType.NONE, false, false, false);
|
||||
public static final Race HUMAN = register("human", Availability.COMMANDS, false, FlightType.NONE, false, false, false);
|
||||
public static final Race EARTH = register("earth", Availability.DEFAULT, false, FlightType.NONE, true, false, false);
|
||||
public static final Race UNICORN = register("unicorn", Availability.DEFAULT, true, FlightType.NONE, false, false, false);
|
||||
public static final Race PEGASUS = register("pegasus", Availability.DEFAULT, false, FlightType.AVIAN, false, false, false);
|
||||
public static final Race BAT = register("bat", Availability.DEFAULT, false, FlightType.AVIAN, false, true, true);
|
||||
public static final Race ALICORN = register("alicorn", Availability.COMMANDS, true, FlightType.AVIAN, true, false, false);
|
||||
public static final Race CHANGELING = register("changeling", Availability.DEFAULT, false, FlightType.INSECTOID, false, false, true);
|
||||
public static final Race KIRIN = register("kirin", Availability.DEFAULT, true, FlightType.NONE, false, false, false);
|
||||
public static final Race HIPPOGRIFF = register("hippogriff", Availability.DEFAULT, false, FlightType.AVIAN, false, false, false);
|
||||
public static final Race SEAPONY = register("seapony", Availability.NONE, false, FlightType.NONE, false, false, false);
|
||||
|
||||
public static void bootstrap() {}
|
||||
|
||||
|
@ -62,8 +69,8 @@ public record Race (Supplier<Composite> compositeSupplier, boolean canCast, Flig
|
|||
return compositeSupplier.get();
|
||||
}
|
||||
|
||||
public Composite composite(@Nullable Race pseudo) {
|
||||
return pseudo == null ? composite() : new Composite(this, pseudo);
|
||||
public Composite composite(@Nullable Race pseudo, @Nullable Race potential) {
|
||||
return pseudo == null && potential == null ? composite() : new Composite(this, pseudo, potential);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,6 +90,10 @@ public record Race (Supplier<Composite> compositeSupplier, boolean canCast, Flig
|
|||
return !isHuman();
|
||||
}
|
||||
|
||||
public boolean isFish() {
|
||||
return this == SEAPONY;
|
||||
}
|
||||
|
||||
public boolean isHuman() {
|
||||
return this == UNSET || this == HUMAN;
|
||||
}
|
||||
|
@ -91,16 +102,12 @@ public record Race (Supplier<Composite> compositeSupplier, boolean canCast, Flig
|
|||
return !isNocturnal();
|
||||
}
|
||||
|
||||
public boolean isOp() {
|
||||
return this == ALICORN;
|
||||
}
|
||||
|
||||
public boolean canFly() {
|
||||
return !flightType().isGrounded();
|
||||
}
|
||||
|
||||
public boolean canInteractWithClouds() {
|
||||
return canFly() && this != CHANGELING && this != BAT;
|
||||
return canFly() && this != CHANGELING && this != BAT && this != HIPPOGRIFF;
|
||||
}
|
||||
|
||||
public Identifier getId() {
|
||||
|
@ -145,6 +152,10 @@ public record Race (Supplier<Composite> compositeSupplier, boolean canCast, Flig
|
|||
return this;
|
||||
}
|
||||
|
||||
public Race or(Race other) {
|
||||
return isEquine() ? this : other;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
|
@ -192,7 +203,7 @@ public record Race (Supplier<Composite> compositeSupplier, boolean canCast, Flig
|
|||
return REGISTRY.stream().filter(r -> r.isPermitted(player)).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public record Composite (Race physical, @Nullable Race pseudo) {
|
||||
public record Composite (Race physical, @Nullable Race pseudo, @Nullable Race potential) {
|
||||
public Race collapsed() {
|
||||
return pseudo == null ? physical : pseudo;
|
||||
}
|
||||
|
|
16
src/main/java/com/minelittlepony/unicopia/UPOIs.java
Normal file
16
src/main/java/com/minelittlepony/unicopia/UPOIs.java
Normal file
|
@ -0,0 +1,16 @@
|
|||
package com.minelittlepony.unicopia;
|
||||
|
||||
import net.fabricmc.fabric.api.object.builder.v1.world.poi.PointOfInterestHelper;
|
||||
import net.minecraft.block.AbstractChestBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.world.poi.PointOfInterestType;
|
||||
|
||||
public interface UPOIs {
|
||||
PointOfInterestType CHESTS = PointOfInterestHelper.register(Unicopia.id("chests"), 1, 64, Registries.BLOCK.getEntrySet().stream()
|
||||
.map(entry -> entry.getValue())
|
||||
.filter(b -> b instanceof AbstractChestBlock)
|
||||
.toArray(Block[]::new));
|
||||
|
||||
static void bootstrap() { }
|
||||
}
|
|
@ -14,6 +14,8 @@ public interface USounds {
|
|||
|
||||
SoundEvent ENTITY_PLAYER_CORRUPTION = PARTICLE_SOUL_ESCAPE;
|
||||
SoundEvent ENTITY_PLAYER_BATPONY_SCREECH = register("entity.player.batpony.screech");
|
||||
SoundEvent ENTITY_PLAYER_HIPPOGRIFF_SCREECH = register("entity.player.hippogriff.screech");
|
||||
SoundEvent ENTITY_PLAYER_HIPPOGRIFF_PECK = ENTITY_CHICKEN_STEP;
|
||||
SoundEvent ENTITY_PLAYER_REBOUND = register("entity.player.rebound");
|
||||
SoundEvent ENTITY_PLAYER_PEGASUS_WINGSFLAP = register("entity.player.pegasus.wingsflap");
|
||||
SoundEvent ENTITY_PLAYER_PEGASUS_FLYING = register("entity.player.pegasus.flying");
|
||||
|
@ -27,6 +29,7 @@ public interface USounds {
|
|||
SoundEvent ENTITY_PLAYER_UNICORN_TELEPORT = register("entity.player.unicorn.teleport");
|
||||
SoundEvent ENTITY_PLAYER_KIRIN_RAGE = ENTITY_POLAR_BEAR_WARNING;
|
||||
SoundEvent ENTITY_PLAYER_KIRIN_RAGE_LOOP = register("entity.player.kirin.rage.loop");
|
||||
SoundEvent ENTITY_PLAYER_SEAPONY_SONAR = register("entity.player.seapony.sonar", 64);
|
||||
|
||||
SoundEvent ENTITY_PLAYER_EARS_RINGING = register("entity.player.ears_ring");
|
||||
SoundEvent ENTITY_PLAYER_HEARTBEAT = register("entity.player.heartbeat");
|
||||
|
@ -146,6 +149,11 @@ public interface USounds {
|
|||
return Registry.register(Registries.SOUND_EVENT, id, SoundEvent.of(id));
|
||||
}
|
||||
|
||||
static SoundEvent register(String name, float range) {
|
||||
Identifier id = Unicopia.id(name);
|
||||
return Registry.register(Registries.SOUND_EVENT, id, SoundEvent.of(id, range));
|
||||
}
|
||||
|
||||
static void bootstrap() {}
|
||||
|
||||
static final class Vanilla extends SoundEvents {}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package com.minelittlepony.unicopia;
|
||||
|
||||
import com.minelittlepony.unicopia.item.toxin.Toxics;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.damage.DamageType;
|
||||
|
@ -27,6 +25,7 @@ public interface UTags {
|
|||
TagKey<Item> SPOOKED_MOB_DROPS = item("spooked_mob_drops");
|
||||
TagKey<Item> IS_DELIVERED_AGGRESSIVELY = item("is_delivered_aggressively");
|
||||
TagKey<Item> FLOATS_ON_CLOUDS = item("floats_on_clouds");
|
||||
TagKey<Item> COOLS_OFF_KIRINS = item("cools_off_kirins");
|
||||
|
||||
TagKey<Item> POLEARMS = item("polearms");
|
||||
TagKey<Item> HORSE_SHOES = item("horse_shoes");
|
||||
|
@ -78,8 +77,4 @@ public interface UTags {
|
|||
static TagKey<DimensionType> dimension(String name) {
|
||||
return TagKey.of(RegistryKeys.DIMENSION_TYPE, new Identifier("c", name));
|
||||
}
|
||||
|
||||
static void bootstrap() {
|
||||
Toxics.bootstrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import com.minelittlepony.unicopia.command.Commands;
|
|||
import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate;
|
||||
import com.minelittlepony.unicopia.container.SpellbookChapterLoader;
|
||||
import com.minelittlepony.unicopia.container.UScreenHandlers;
|
||||
import com.minelittlepony.unicopia.diet.DietsLoader;
|
||||
import com.minelittlepony.unicopia.diet.affliction.AfflictionType;
|
||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
||||
import com.minelittlepony.unicopia.entity.effect.UPotions;
|
||||
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||
|
@ -64,7 +66,6 @@ public class Unicopia implements ModInitializer {
|
|||
@Override
|
||||
public void onInitialize() {
|
||||
Channel.bootstrap();
|
||||
UTags.bootstrap();
|
||||
UCriteria.bootstrap();
|
||||
UEntities.bootstrap();
|
||||
Commands.bootstrap();
|
||||
|
@ -83,20 +84,18 @@ public class Unicopia implements ModInitializer {
|
|||
});
|
||||
NocturnalSleepManager.bootstrap();
|
||||
|
||||
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(TreeTypeLoader.INSTANCE);
|
||||
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(UEnchantments.POISONED_JOKE);
|
||||
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(new TraitLoader());
|
||||
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(StateMapLoader.INSTANCE);
|
||||
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(SpellbookChapterLoader.INSTANCE);
|
||||
registerServerDataReloaders(ResourceManagerHelper.get(ResourceType.SERVER_DATA));
|
||||
|
||||
UGameEvents.bootstrap();
|
||||
UBlocks.bootstrap();
|
||||
UPOIs.bootstrap();
|
||||
UItems.bootstrap();
|
||||
UPotions.bootstrap();
|
||||
UParticles.bootstrap();
|
||||
USounds.bootstrap();
|
||||
Race.bootstrap();
|
||||
SpellType.bootstrap();
|
||||
AfflictionType.bootstrap();
|
||||
Abilities.bootstrap();
|
||||
UScreenHandlers.bootstrap();
|
||||
UWorldGen.bootstrap();
|
||||
|
@ -104,6 +103,15 @@ public class Unicopia implements ModInitializer {
|
|||
UDamageTypes.bootstrap();
|
||||
}
|
||||
|
||||
private void registerServerDataReloaders(ResourceManagerHelper registry) {
|
||||
registry.registerReloadListener(TreeTypeLoader.INSTANCE);
|
||||
registry.registerReloadListener(UEnchantments.POISONED_JOKE);
|
||||
registry.registerReloadListener(new TraitLoader());
|
||||
registry.registerReloadListener(StateMapLoader.INSTANCE);
|
||||
registry.registerReloadListener(SpellbookChapterLoader.INSTANCE);
|
||||
registry.registerReloadListener(new DietsLoader());
|
||||
}
|
||||
|
||||
public interface SidedAccess {
|
||||
Optional<Pony> getPony();
|
||||
|
||||
|
|
|
@ -35,6 +35,9 @@ public class UnicopiaMixinPlugin implements IMixinConfigPlugin {
|
|||
if (mixinClassName.indexOf("ad_astra") != -1) {
|
||||
return FabricLoader.getInstance().isModLoaded("ad_astra");
|
||||
}
|
||||
if (mixinClassName.indexOf("minelp") != -1) {
|
||||
return FabricLoader.getInstance().isModLoaded("minelp");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@ public interface Abilities {
|
|||
.toList();
|
||||
});
|
||||
|
||||
// all races
|
||||
Ability<?> CHANGE_FORM = register(new ChangeFormAbility(), "change_form", AbilitySlot.PRIMARY);
|
||||
|
||||
// unicorn / alicorn
|
||||
Ability<?> CAST = register(new UnicornCastingAbility(), "cast", AbilitySlot.PRIMARY);
|
||||
Ability<?> SHOOT = register(new UnicornProjectileAbility(), "shoot", AbilitySlot.PRIMARY);
|
||||
|
@ -42,9 +45,14 @@ public interface Abilities {
|
|||
Ability<?> RAINBOOM = register(new PegasusRainboomAbility(), "rainboom", AbilitySlot.PRIMARY);
|
||||
Ability<?> CAPTURE_CLOUD = register(new PegasusCaptureStormAbility(), "capture_cloud", AbilitySlot.SECONDARY);
|
||||
|
||||
// pegasus / bat / alicorn / changeling
|
||||
// hippogriff
|
||||
Ability<?> DASH = register(new FlyingDashAbility(), "dash", AbilitySlot.PRIMARY);
|
||||
Ability<?> SCREECH = register(new ScreechAbility(), "screech", AbilitySlot.SECONDARY);
|
||||
Ability<?> PECK = register(new PeckAbility(), "peck", AbilitySlot.SECONDARY);
|
||||
|
||||
// pegasus / bat / alicorn / changeling / hippogriff
|
||||
Ability<?> CARRY = register(new CarryAbility(), "carry", AbilitySlot.PRIMARY);
|
||||
Ability<?> TOGGLE_FLIGHT = register(new PegasusFlightToggleAbility(), "toggle_flight", AbilitySlot.TERTIARY);
|
||||
Ability<?> TOGGLE_FLIGHT = register(new ToggleFlightAbility(), "toggle_flight", AbilitySlot.TERTIARY);
|
||||
|
||||
// changeling
|
||||
Ability<?> DISGUISE = register(new ChangelingDisguiseAbility(), "disguise", AbilitySlot.SECONDARY);
|
||||
|
@ -59,6 +67,9 @@ public interface Abilities {
|
|||
Ability<?> NIRIK_BLAST = register(new NirikBlastAbility(), "nirik_blast", AbilitySlot.SECONDARY);
|
||||
Ability<?> KIRIN_CAST = register(new KirinCastingAbility(), "kirin_cast", AbilitySlot.SECONDARY);
|
||||
|
||||
// seapony
|
||||
Ability<?> SONAR_PULSE = register(new SeaponySonarPulseAbility(), "sonar_pulse", AbilitySlot.SECONDARY);
|
||||
|
||||
static <T extends Ability<?>> T register(T power, String name, AbilitySlot slot) {
|
||||
Identifier id = Unicopia.id(name);
|
||||
BY_SLOT.computeIfAbsent(slot, s -> new LinkedHashSet<>()).add(power);
|
||||
|
|
|
@ -179,6 +179,10 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cooldown > 100 && player.asEntity().isCreative()) {
|
||||
cooldown = Math.max(10, cooldown - 100);
|
||||
}
|
||||
|
||||
if (cooldown > 0 && cooldown-- > 0) {
|
||||
ability.coolDown(player, slot);
|
||||
|
||||
|
|
|
@ -1,55 +1,20 @@
|
|||
package com.minelittlepony.unicopia.ability;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.minelittlepony.unicopia.AwaitTickQueue;
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.UTags;
|
||||
import com.minelittlepony.unicopia.ability.data.Numeric;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.advancement.UCriteria;
|
||||
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
|
||||
import com.minelittlepony.unicopia.entity.Living;
|
||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.util.RegistryUtils;
|
||||
import com.minelittlepony.unicopia.util.VecHelper;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.event.GameEvent;
|
||||
|
||||
/**
|
||||
* A magic casting ability for unicorns.
|
||||
* (only shields for now)
|
||||
* An ability to screeeeeeeeEeEeEeeee!
|
||||
*/
|
||||
public class BatEeeeAbility implements Ability<Numeric> {
|
||||
public class BatEeeeAbility extends ScreechAbility {
|
||||
public static final int SELF_SPOOK_PROBABILITY = 20000;
|
||||
public static final int MOB_SPOOK_PROBABILITY = 1000;
|
||||
|
||||
@Override
|
||||
public int getWarmupTime(Pony player) {
|
||||
return 30;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCooldownTime(Pony player) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCostEstimate(Pony player) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateOnEarlyRelease() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Race race) {
|
||||
|
@ -57,22 +22,7 @@ public class BatEeeeAbility implements Ability<Numeric> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Optional<Numeric> prepare(Pony player) {
|
||||
return player.getAbilities().getActiveStat()
|
||||
.map(stat -> (int)(stat.getWarmup() * getWarmupTime(player)))
|
||||
.filter(i -> i >= 0)
|
||||
.map(Numeric::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Numeric.Serializer<Numeric> getSerializer() {
|
||||
return Numeric.SERIALIZER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pony player, Numeric data) {
|
||||
float strength = 1 - MathHelper.clamp(data.type() / (float)getWarmupTime(player), 0, 1);
|
||||
Random rng = player.asWorld().random;
|
||||
protected void playSounds(Pony player, Random rng, float strength) {
|
||||
int count = 1 + rng.nextInt(10) + (int)(strength * 10);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
@ -81,6 +31,7 @@ public class BatEeeeAbility implements Ability<Numeric> {
|
|||
1.6F + (rng.nextFloat() - 0.5F)
|
||||
);
|
||||
}
|
||||
player.asWorld().emitGameEvent(player.asEntity(), GameEvent.ENTITY_ACTION, player.asEntity().getEyePos());
|
||||
for (int j = 0; j < (int)(strength * 2); j++) {
|
||||
for (int k = 0; k < count; k++) {
|
||||
AwaitTickQueue.scheduleTask(player.asWorld(), w -> {
|
||||
|
@ -88,59 +39,14 @@ public class BatEeeeAbility implements Ability<Numeric> {
|
|||
(0.9F + (rng.nextFloat() - 0.5F) / 2F) * strength,
|
||||
1.6F + (rng.nextFloat() - 0.5F)
|
||||
);
|
||||
player.asWorld().emitGameEvent(player.asEntity(), GameEvent.ENTITY_ACTION, player.asEntity().getEyePos());
|
||||
}, rng.nextInt(3));
|
||||
}
|
||||
}
|
||||
|
||||
if (!player.getPhysics().isFlying()) {
|
||||
player.setAnimation(Animation.SPREAD_WINGS, Animation.Recipient.ANYONE);
|
||||
}
|
||||
|
||||
Vec3d origin = player.getOriginVector();
|
||||
|
||||
if (strength > 0.5F && rng.nextInt(SELF_SPOOK_PROBABILITY) == 0) {
|
||||
player.asEntity().damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), 0.1F);
|
||||
UCriteria.SCREECH_SELF.trigger(player.asEntity());
|
||||
}
|
||||
|
||||
int total = player.findAllEntitiesInRange((int)Math.max(1, 8 * strength)).mapToInt(e -> {
|
||||
if (e instanceof LivingEntity living && !SpellType.SHIELD.isOn(e)) {
|
||||
boolean isEarthPony = EquinePredicates.PLAYER_EARTH.test(e);
|
||||
e.damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), isEarthPony ? 0.1F : 0.3F);
|
||||
if (e.getWorld().random.nextInt(MOB_SPOOK_PROBABILITY) == 0) {
|
||||
RegistryUtils.pickRandom(e.getWorld(), UTags.SPOOKED_MOB_DROPS).ifPresent(drop -> {
|
||||
e.dropStack(drop.getDefaultStack());
|
||||
e.playSound(USounds.Vanilla.ENTITY_ITEM_PICKUP, 1, 0.1F);
|
||||
UCriteria.SPOOK_MOB.trigger(player.asEntity());
|
||||
});
|
||||
}
|
||||
|
||||
Vec3d knockVec = origin.subtract(e.getPos()).multiply(strength);
|
||||
living.takeKnockback((isEarthPony ? 0.3F : 0.5F) * strength, knockVec.getX(), knockVec.getZ());
|
||||
if (!isEarthPony) {
|
||||
e.addVelocity(0, 0.1 * strength, 0);
|
||||
}
|
||||
Living.updateVelocity(e);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}).sum();
|
||||
|
||||
if (total >= 20) {
|
||||
UCriteria.SCREECH_TWENTY_MOBS.trigger(player.asEntity());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warmUp(Pony player, AbilitySlot slot) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void coolDown(Pony player, AbilitySlot slot) {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
player.addParticle(ParticleTypes.BUBBLE_POP, player.getPhysics().getHeadPosition().toCenterPos(), VecHelper.supply(() -> player.asWorld().getRandom().nextGaussian() - 0.5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,13 +38,13 @@ public class BatPonyHangAbility implements Ability<Multi> {
|
|||
@Override
|
||||
public Optional<Multi> prepare(Pony player) {
|
||||
|
||||
if (player.isHanging()) {
|
||||
if (player.getAcrobatics().isHanging()) {
|
||||
return Optional.of(new Multi(BlockPos.ZERO, 0));
|
||||
}
|
||||
|
||||
return TraceHelper.findBlock(player.asEntity(), 5, 1)
|
||||
.map(BlockPos::down)
|
||||
.filter(player::canHangAt)
|
||||
.filter(player.getAcrobatics()::canHangAt)
|
||||
.map(pos -> new Multi(pos, 1));
|
||||
}
|
||||
|
||||
|
@ -55,13 +55,13 @@ public class BatPonyHangAbility implements Ability<Multi> {
|
|||
|
||||
@Override
|
||||
public boolean apply(Pony player, Multi data) {
|
||||
if (data.hitType() == 0 && player.isHanging()) {
|
||||
player.stopHanging();
|
||||
if (data.hitType() == 0 && player.getAcrobatics().isHanging()) {
|
||||
player.getAcrobatics().stopHanging();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data.hitType() == 1 && player.canHangAt(data.pos().pos())) {
|
||||
player.startHanging(data.pos().pos());
|
||||
if (data.hitType() == 1 && player.getAcrobatics().canHangAt(data.pos().pos())) {
|
||||
player.getAcrobatics().startHanging(data.pos().pos());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
package com.minelittlepony.unicopia.ability;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
|
||||
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class ChangeFormAbility implements Ability<Hit> {
|
||||
|
||||
@Override
|
||||
public int getWarmupTime(Pony player) {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCooldownTime(Pony player) {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Race.Composite race) {
|
||||
return race.potential() != null && race.potential() != race.physical();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Race race) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getIcon(Pony player) {
|
||||
Race potential = player.getCompositeRace().potential();
|
||||
if (potential == null) {
|
||||
return Ability.super.getIcon(player);
|
||||
}
|
||||
return getId().withPath(p -> "textures/gui/ability/" + p + "_" + potential.getId().getPath() + ".png");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Optional<Hit> prepare(Pony player) {
|
||||
return Hit.of(canUse(player.getCompositeRace()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hit.Serializer<Hit> getSerializer() {
|
||||
return Hit.SERIALIZER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCostEstimate(Pony player) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pony player, Hit data) {
|
||||
if (prepare(player).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Pony> targets = getTargets(player).toList();
|
||||
player.subtractEnergyCost(5 * targets.size());
|
||||
boolean isTransforming = player.getSuppressedRace().isUnset();
|
||||
targets.forEach(target -> {
|
||||
Race supressed = target.getSuppressedRace();
|
||||
if (target == player || supressed.isUnset() == isTransforming) {
|
||||
Race actualRace = target.getSpecies();
|
||||
target.setSpecies(supressed.or(player.getCompositeRace().potential()));
|
||||
target.setSuppressedRace(isTransforming ? actualRace : Race.UNSET);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warmUp(Pony player, AbilitySlot slot) {
|
||||
player.getMagicalReserves().getExertion().addPercent(6);
|
||||
getTargets(player).forEach(target -> {
|
||||
if (player.getAbilities().getStat(slot).getWarmup() % 5 == 0) {
|
||||
player.asWorld().playSound(target.asEntity(), target.getOrigin(), SoundEvents.BLOCK_BUBBLE_COLUMN_WHIRLPOOL_INSIDE, SoundCategory.PLAYERS);
|
||||
}
|
||||
|
||||
if (player.asWorld().random.nextInt(5) == 0) {
|
||||
player.asWorld().playSound(target.asEntity(), target.getOrigin(), USounds.Vanilla.BLOCK_BUBBLE_COLUMN_BUBBLE_POP, SoundCategory.PLAYERS);
|
||||
}
|
||||
|
||||
target.spawnParticles(ParticleTypes.BUBBLE_COLUMN_UP, 15);
|
||||
target.spawnParticles(ParticleTypes.BUBBLE_POP, 15);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void coolDown(Pony player, AbilitySlot slot) {
|
||||
}
|
||||
|
||||
private Stream<Pony> getTargets(Pony player) {
|
||||
return Stream.concat(Stream.of(player), FriendshipBraceletItem.getPartyMembers(player, 3));
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.ability.data.Hit;
|
|||
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.entity.behaviour.Disguise;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.mixin.MixinFallingBlockEntity;
|
||||
import com.minelittlepony.unicopia.particle.UParticles;
|
||||
|
@ -50,9 +51,13 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
|
|||
|
||||
player.getEntityWorld().playSound(null, player.getBlockPos(), USounds.ENTITY_PLAYER_CHANGELING_TRANSFORM, SoundCategory.PLAYERS, 1.4F, 0.4F);
|
||||
|
||||
iplayer.getSpellSlot().get(SpellType.CHANGELING_DISGUISE, true)
|
||||
.orElseGet(() -> SpellType.CHANGELING_DISGUISE.withTraits().apply(iplayer, CastingMethod.INNATE))
|
||||
.setDisguise(looked);
|
||||
Disguise currentDisguise = iplayer.getSpellSlot().get(SpellType.CHANGELING_DISGUISE, true)
|
||||
.orElseGet(() -> SpellType.CHANGELING_DISGUISE.withTraits().apply(iplayer, CastingMethod.INNATE));
|
||||
|
||||
if (currentDisguise.isOf(looked)) {
|
||||
looked = null;
|
||||
}
|
||||
currentDisguise.setDisguise(looked);
|
||||
|
||||
if (!player.isCreative()) {
|
||||
iplayer.getMagicalReserves().getMana().multiply(0.1F);
|
||||
|
@ -65,7 +70,7 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
|
|||
|
||||
@Override
|
||||
public void warmUp(Pony player, AbilitySlot slot) {
|
||||
player.getMagicalReserves().getEnergy().add(20);
|
||||
player.getMagicalReserves().getEnergy().add(2F);
|
||||
player.spawnParticles(UParticles.CHANGELING_MAGIC, 5);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,12 @@ import java.util.Optional;
|
|||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.ability.data.Pos;
|
||||
import com.minelittlepony.unicopia.block.UBlocks;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
||||
import com.minelittlepony.unicopia.util.TraceHelper;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.item.BoneMealItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
|
@ -71,8 +73,21 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
|
|||
|
||||
ItemStack stack = new ItemStack(Items.BONE_MEAL);
|
||||
|
||||
if (BoneMealItem.useOnFertilizable(stack, w, pos)
|
||||
|| BoneMealItem.useOnGround(stack, w, pos, Direction.UP)) {
|
||||
if (state.getBlock() instanceof Growable growable) {
|
||||
return growable.grow(w, state, pos) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (BoneMealItem.useOnFertilizable(stack, w, pos)) {
|
||||
if (w.random.nextInt(350) == 0) {
|
||||
if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) {
|
||||
w.setBlockState(pos.down(), Blocks.DIRT.getDefaultState());
|
||||
}
|
||||
w.setBlockState(pos, UBlocks.PLUNDER_VINE_BUD.getDefaultState());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (BoneMealItem.useOnGround(stack, w, pos, Direction.UP)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -92,4 +107,8 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
|
|||
public void coolDown(Pony player, AbilitySlot slot) {
|
||||
|
||||
}
|
||||
|
||||
public interface Growable {
|
||||
boolean grow(World world, BlockState state, BlockPos pos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,8 +60,7 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
|
||||
@Override
|
||||
public Identifier getIcon(Pony player) {
|
||||
Identifier id = Abilities.REGISTRY.getId(this);
|
||||
return new Identifier(id.getNamespace(), "textures/gui/ability/" + id.getPath()
|
||||
return getId().withPath(p -> "textures/gui/ability/" + p
|
||||
+ "_" + player.getObservedSpecies().getId().getPath()
|
||||
+ "_" + (getKickDirection(player) > 0 ? "forward" : "backward")
|
||||
+ ".png");
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package com.minelittlepony.unicopia.ability;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
|
||||
/**
|
||||
* Dashing ability for flying creatures.
|
||||
*/
|
||||
public class FlyingDashAbility implements Ability<Hit> {
|
||||
|
||||
@Override
|
||||
public int getWarmupTime(Pony player) {
|
||||
return 19;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCooldownTime(Pony player) {
|
||||
return 30;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Race race) {
|
||||
return race == Race.HIPPOGRIFF;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Optional<Hit> prepare(Pony player) {
|
||||
return Hit.of(player.getPhysics().isFlying());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hit.Serializer<Hit> getSerializer() {
|
||||
return Hit.SERIALIZER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCostEstimate(Pony player) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQuickAction(Pony player, ActivationType type, Optional<Hit> data) {
|
||||
|
||||
if (type == ActivationType.TAP && !player.getMotion().isRainbooming() && player.getPhysics().isFlying() && player.getMagicalReserves().getMana().get() > 40) {
|
||||
player.getPhysics().dashForward((float)player.asWorld().random.nextTriangular(2.5F, 0.3F));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pony player, Hit data) {
|
||||
player.getPhysics().dashForward((float)player.asWorld().random.nextTriangular(2.5F, 0.3F));
|
||||
player.subtractEnergyCost(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warmUp(Pony player, AbilitySlot slot) {
|
||||
player.getMagicalReserves().getExertion().addPercent(6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void coolDown(Pony player, AbilitySlot slot) {
|
||||
float velocityScale = player.getAbilities().getStat(slot).getCooldown();
|
||||
float multiplier = 1 + (0.02F * velocityScale);
|
||||
player.asEntity().getVelocity().multiply(multiplier, 1, multiplier);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package com.minelittlepony.unicopia.ability;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.ability.data.Numeric;
|
||||
import com.minelittlepony.unicopia.entity.Living;
|
||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.util.TraceHelper;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.predicate.entity.EntityPredicates;
|
||||
import net.minecraft.registry.tag.BlockTags;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldEvents;
|
||||
import net.minecraft.world.event.GameEvent;
|
||||
|
||||
/**
|
||||
* Hippogriff ability to use their beak as a weapon
|
||||
*/
|
||||
public class PeckAbility implements Ability<Hit> {
|
||||
|
||||
@Override
|
||||
public int getWarmupTime(Pony player) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCooldownTime(Pony player) {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCostEstimate(Pony player) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateOnEarlyRelease() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Race race) {
|
||||
return race == Race.HIPPOGRIFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Hit> prepare(Pony player) {
|
||||
return Hit.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Numeric.Serializer<Hit> getSerializer() {
|
||||
return Hit.SERIALIZER;
|
||||
}
|
||||
|
||||
protected LivingEntity findTarget(PlayerEntity player, World w) {
|
||||
return TraceHelper.<LivingEntity>findEntity(player, 5, 1, hit -> {
|
||||
return EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.test(hit) && !player.isConnectedThroughVehicle(hit);
|
||||
}).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pony player, Hit hit) {
|
||||
LivingEntity target = findTarget(player.asEntity(), player.asWorld());
|
||||
|
||||
playSounds(player, player.asWorld().getRandom(), 1);
|
||||
|
||||
if (target != null) {
|
||||
spookMob(player, target, 1);
|
||||
} else {
|
||||
BlockPos pos = TraceHelper.findBlock(player.asEntity(), 5, 1).orElse(BlockPos.ORIGIN);
|
||||
if (pos != BlockPos.ORIGIN) {
|
||||
BlockState state = player.asWorld().getBlockState(pos);
|
||||
if (state.isReplaceable()) {
|
||||
player.asWorld().breakBlock(pos, true);
|
||||
} else if (state.isIn(BlockTags.DIRT) || player.asWorld().random.nextInt(40000) == 0) {
|
||||
player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, pos, Block.getRawIdFromState(state));
|
||||
pos = pos.up();
|
||||
World world = player.asWorld();
|
||||
|
||||
if (world instanceof ServerWorld sw) {
|
||||
for (ItemStack stack : Block.getDroppedStacks(state, sw, pos, null)) {
|
||||
if (Block.getBlockFromItem(stack.getItem()) == Blocks.AIR) {
|
||||
Block.dropStack(world, pos, stack);
|
||||
}
|
||||
}
|
||||
state.onStacksDropped(sw, pos, ItemStack.EMPTY, true);
|
||||
|
||||
if (world.random.nextInt(20) == 0) {
|
||||
world.breakBlock(pos.down(), false);
|
||||
player.asEntity().sendMessage(Text.translatable("ability.unicopia.peck.block.fled"), true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
player.asEntity().sendMessage(Text.translatable("ability.unicopia.peck.block.unfased"), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void playSounds(Pony player, Random rng, float strength) {
|
||||
player.getMagicalReserves().getExertion().addPercent(100);
|
||||
player.getMagicalReserves().getEnergy().addPercent(10);
|
||||
player.playSound(USounds.Vanilla.ENTITY_CHICKEN_HURT,
|
||||
1,
|
||||
0.9F + (rng.nextFloat() - 0.5F)
|
||||
);
|
||||
player.asWorld().emitGameEvent(player.asEntity(), GameEvent.STEP, player.asEntity().getEyePos());
|
||||
}
|
||||
|
||||
protected void spookMob(Pony player, LivingEntity living, float strength) {
|
||||
boolean isEarthPony = EquinePredicates.PLAYER_EARTH.test(living);
|
||||
boolean isBracing = isEarthPony && player.asEntity().isSneaking();
|
||||
|
||||
if (!isBracing) {
|
||||
living.damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), isEarthPony ? 0.1F : 0.3F);
|
||||
}
|
||||
|
||||
Vec3d knockVec = player.getOriginVector().subtract(living.getPos()).multiply(strength);
|
||||
living.takeKnockback((isBracing ? 0.2F : isEarthPony ? 0.3F : 0.5F) * strength, knockVec.getX(), knockVec.getZ());
|
||||
if (!isEarthPony) {
|
||||
living.addVelocity(0, 0.1 * strength, 0);
|
||||
}
|
||||
Living.updateVelocity(living);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warmUp(Pony player, AbilitySlot slot) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void coolDown(Pony player, AbilitySlot slot) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package com.minelittlepony.unicopia.ability;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.UTags;
|
||||
import com.minelittlepony.unicopia.ability.data.Numeric;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.advancement.UCriteria;
|
||||
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
|
||||
import com.minelittlepony.unicopia.entity.Living;
|
||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.util.RegistryUtils;
|
||||
import com.minelittlepony.unicopia.util.VecHelper;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.event.GameEvent;
|
||||
|
||||
/**
|
||||
* An ability to scream very loud
|
||||
*/
|
||||
public class ScreechAbility implements Ability<Numeric> {
|
||||
public static final int MOB_SPOOK_PROBABILITY = 1000;
|
||||
|
||||
@Override
|
||||
public int getWarmupTime(Pony player) {
|
||||
return 30;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCooldownTime(Pony player) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCostEstimate(Pony player) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateOnEarlyRelease() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Race race) {
|
||||
return race == Race.HIPPOGRIFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Numeric> prepare(Pony player) {
|
||||
return player.getAbilities().getActiveStat()
|
||||
.map(stat -> (int)(stat.getWarmup() * getWarmupTime(player)))
|
||||
.filter(i -> i >= 0)
|
||||
.map(Numeric::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Numeric.Serializer<Numeric> getSerializer() {
|
||||
return Numeric.SERIALIZER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pony player, Numeric data) {
|
||||
float strength = 1 - MathHelper.clamp(data.type() / (float)getWarmupTime(player), 0, 1);
|
||||
Random rng = player.asWorld().random;
|
||||
|
||||
playSounds(player, rng, strength);
|
||||
|
||||
if (!player.getPhysics().isFlying()) {
|
||||
player.setAnimation(Animation.SPREAD_WINGS, Animation.Recipient.ANYONE);
|
||||
}
|
||||
|
||||
int total = player.findAllEntitiesInRange((int)Math.max(1, 8 * strength)).mapToInt(e -> {
|
||||
if (e instanceof LivingEntity living && !SpellType.SHIELD.isOn(e)) {
|
||||
spookMob(player, living, strength);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}).sum();
|
||||
|
||||
if (total >= 20) {
|
||||
UCriteria.SCREECH_TWENTY_MOBS.trigger(player.asEntity());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void playSounds(Pony player, Random rng, float strength) {
|
||||
player.playSound(USounds.ENTITY_PLAYER_HIPPOGRIFF_SCREECH,
|
||||
(1.2F + (rng.nextFloat() - 0.5F) / 2F) * strength,
|
||||
1.1F + (rng.nextFloat() - 0.5F)
|
||||
);
|
||||
player.asWorld().emitGameEvent(player.asEntity(), GameEvent.ENTITY_ACTION, player.asEntity().getEyePos());
|
||||
}
|
||||
|
||||
protected void spookMob(Pony player, LivingEntity living, float strength) {
|
||||
boolean isEarthPony = EquinePredicates.PLAYER_EARTH.test(living);
|
||||
boolean isBracing = isEarthPony && player.asEntity().isSneaking();
|
||||
|
||||
if (!isBracing) {
|
||||
living.damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), isEarthPony ? 0.1F : 0.3F);
|
||||
|
||||
|
||||
if (living.getWorld().random.nextInt(MOB_SPOOK_PROBABILITY) == 0) {
|
||||
RegistryUtils.pickRandom(living.getWorld(), UTags.SPOOKED_MOB_DROPS).ifPresent(drop -> {
|
||||
living.dropStack(drop.getDefaultStack());
|
||||
living.playSound(USounds.Vanilla.ENTITY_ITEM_PICKUP, 1, 0.1F);
|
||||
UCriteria.SPOOK_MOB.trigger(player.asEntity());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Vec3d knockVec = player.getOriginVector().subtract(living.getPos()).multiply(strength);
|
||||
living.takeKnockback((isBracing ? 0.2F : isEarthPony ? 0.3F : 0.5F) * strength, knockVec.getX(), knockVec.getZ());
|
||||
if (!isEarthPony) {
|
||||
living.addVelocity(0, 0.1 * strength, 0);
|
||||
}
|
||||
Living.updateVelocity(living);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warmUp(Pony player, AbilitySlot slot) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void coolDown(Pony player, AbilitySlot slot) {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
player.addParticle(ParticleTypes.BUBBLE_POP, player.asEntity().getEyePos(),
|
||||
VecHelper.supply(() -> (player.asWorld().getRandom().nextGaussian() - 0.5) * 0.3)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package com.minelittlepony.unicopia.ability;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.AwaitTickQueue;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.UPOIs;
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||
import com.minelittlepony.unicopia.particle.UParticles;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityGroup;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.mob.HostileEntity;
|
||||
import net.minecraft.registry.tag.FluidTags;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.event.GameEvent;
|
||||
import net.minecraft.world.poi.PointOfInterestStorage.OccupationStatus;
|
||||
|
||||
public class SeaponySonarPulseAbility implements Ability<Hit> {
|
||||
|
||||
@Override
|
||||
public int getWarmupTime(Pony player) {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCooldownTime(Pony player) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Race race) {
|
||||
return race == Race.SEAPONY;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Optional<Hit> prepare(Pony player) {
|
||||
return Hit.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hit.Serializer<Hit> getSerializer() {
|
||||
return Hit.SERIALIZER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCostEstimate(Pony player) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pony player, Hit data) {
|
||||
player.setAnimation(Animation.ARMS_UP, Animation.Recipient.ANYONE);
|
||||
|
||||
for (Entity target : player.findAllEntitiesInRange(64, e -> {
|
||||
return (e instanceof LivingEntity && (e instanceof HostileEntity || ((LivingEntity)e).getGroup() == EntityGroup.AQUATIC)) && e.isSubmergedInWater();
|
||||
}).sorted(Comparator.comparing(e -> e.distanceTo(player.asEntity()))).toList()) {
|
||||
Vec3d offset = target.getPos().subtract(player.getOriginVector());
|
||||
float distance = target.distanceTo(player.asEntity());
|
||||
if (distance < 4) {
|
||||
float scale = 1 - (distance/10F);
|
||||
((LivingEntity)target).takeKnockback(0.7 * scale, -offset.x, -offset.z);
|
||||
target.damage(target.getDamageSources().sonicBoom(player.asEntity()), 10 * scale);
|
||||
} else {
|
||||
emitPing(player, target.getPos(), 10, 1, 1.3F);
|
||||
}
|
||||
}
|
||||
player.subtractEnergyCost(5);
|
||||
|
||||
if (player.asWorld() instanceof ServerWorld sw) {
|
||||
sw.getPointOfInterestStorage().getNearestPosition(
|
||||
type -> type.value() == UPOIs.CHESTS,
|
||||
pos -> player.asWorld().getFluidState(pos).isIn(FluidTags.WATER), player.getOrigin(), 64, OccupationStatus.ANY)
|
||||
.ifPresent(chestPos -> {
|
||||
emitPing(player, chestPos.toCenterPos(), 20, 0.5F, 2F);
|
||||
});
|
||||
}
|
||||
|
||||
player.playSound(USounds.ENTITY_PLAYER_SEAPONY_SONAR, 1);
|
||||
player.spawnParticles(UParticles.SHOCKWAVE, 1);
|
||||
player.asEntity().emitGameEvent(GameEvent.INSTRUMENT_PLAY);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void emitPing(Pony player, Vec3d pos, int delay, float volume, float pitch) {
|
||||
AwaitTickQueue.scheduleTask(player.asWorld(), w -> {
|
||||
ParticleUtils.spawnParticle(w, UParticles.SHOCKWAVE, pos, Vec3d.ZERO);
|
||||
float loudness = Math.max(0, 1.4F - (float)Math.log10(player.getOriginVector().distanceTo(pos)));
|
||||
w.playSound(null, pos.x, pos.y, pos.z, USounds.ENTITY_PLAYER_SEAPONY_SONAR, SoundCategory.AMBIENT, volume * loudness, pitch);
|
||||
w.emitGameEvent(player.asEntity(), GameEvent.INSTRUMENT_PLAY, pos);
|
||||
}, delay + (int)player.getOriginVector().distanceTo(pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warmUp(Pony player, AbilitySlot slot) {
|
||||
player.getMagicalReserves().getExertion().addPercent(6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void coolDown(Pony player, AbilitySlot slot) {
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import com.minelittlepony.unicopia.entity.player.Pony;
|
|||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class PegasusFlightToggleAbility implements Ability<Hit> {
|
||||
public class ToggleFlightAbility implements Ability<Hit> {
|
||||
|
||||
@Override
|
||||
public int getWarmupTime(Pony player) {
|
|
@ -8,6 +8,7 @@ import com.minelittlepony.unicopia.ability.data.Hit;
|
|||
import com.minelittlepony.unicopia.ability.data.Pos;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.advancement.UCriteria;
|
||||
import com.minelittlepony.unicopia.block.state.StatePredicate;
|
||||
import com.minelittlepony.unicopia.entity.Living;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
|
@ -163,16 +164,19 @@ public class UnicornTeleportAbility implements Ability<Pos> {
|
|||
|
||||
Vec3d dest = destination.vec().add(offset);
|
||||
|
||||
participant.teleport(
|
||||
dest.x,
|
||||
getTargetYPosition(participant.getEntityWorld(), BlockPos.ofFloored(dest), ShapeContext.of(participant)),
|
||||
dest.z
|
||||
);
|
||||
dest = new Vec3d(dest.x, getTargetYPosition(participant.getEntityWorld(), BlockPos.ofFloored(dest), ShapeContext.of(participant)), dest.z);
|
||||
|
||||
participant.teleport(dest.x, dest.y, dest.z);
|
||||
teleporter.subtractEnergyCost(distance);
|
||||
|
||||
participant.fallDistance /= distance;
|
||||
|
||||
participant.getWorld().playSound(null, destination.pos(), USounds.ENTITY_PLAYER_UNICORN_TELEPORT, SoundCategory.PLAYERS, 1, 1);
|
||||
BlockPos blockPos = BlockPos.ofFloored(dest);
|
||||
|
||||
participant.getWorld().playSound(null, blockPos, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, SoundCategory.PLAYERS, 1, 1);
|
||||
if (!participant.getEntityWorld().isInBuildLimit(blockPos)) {
|
||||
UCriteria.TELEPORT_ABOVE_WORLD.trigger(teleporter.asEntity());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -30,11 +30,17 @@ public class RageAbilitySpell extends AbstractSpell {
|
|||
private int age;
|
||||
private int ticksExtenguishing;
|
||||
|
||||
private int ticksToExtenguish;
|
||||
|
||||
public RageAbilitySpell(CustomisedSpellType<?> type) {
|
||||
super(type);
|
||||
setHidden(true);
|
||||
}
|
||||
|
||||
public void setExtenguishing() {
|
||||
ticksToExtenguish += 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tick(Caster<?> source, Situation situation) {
|
||||
|
||||
|
@ -42,7 +48,7 @@ public class RageAbilitySpell extends AbstractSpell {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (source.asEntity().isInsideWaterOrBubbleColumn()) {
|
||||
if (source.asEntity().isInsideWaterOrBubbleColumn() || source.asEntity().isFrozen() || ticksToExtenguish > 0) {
|
||||
ticksExtenguishing++;
|
||||
source.playSound(SoundEvents.ENTITY_GENERIC_EXTINGUISH_FIRE, 1);
|
||||
source.spawnParticles(ParticleTypes.CLOUD, 12);
|
||||
|
@ -51,6 +57,10 @@ public class RageAbilitySpell extends AbstractSpell {
|
|||
ticksExtenguishing = 0;
|
||||
}
|
||||
|
||||
if (ticksToExtenguish > 0) {
|
||||
ticksToExtenguish--;
|
||||
}
|
||||
|
||||
if (ticksExtenguishing > 10) {
|
||||
return false;
|
||||
}
|
||||
|
@ -94,7 +104,7 @@ public class RageAbilitySpell extends AbstractSpell {
|
|||
}
|
||||
|
||||
if (source instanceof Pony pony) {
|
||||
if (source.isClient() && pony.asEntity().getAttackCooldownProgress(0) == 0) {
|
||||
if (pony.isClientPlayer() && pony.asEntity().getAttackCooldownProgress(0) == 0) {
|
||||
InteractionManager.instance().playLoopingSound(source.asEntity(), InteractionManager.SOUND_KIRIN_RAGE, source.asWorld().random.nextLong());
|
||||
}
|
||||
Bar energyBar = pony.getMagicalReserves().getEnergy();
|
||||
|
|
|
@ -51,7 +51,7 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
|
|||
);
|
||||
|
||||
public static void load(Map<Identifier, SpellTraits> newRegistry) {
|
||||
REGISTRY = new HashMap<>(newRegistry);
|
||||
REGISTRY = newRegistry;
|
||||
ITEMS.clear();
|
||||
REGISTRY.forEach((itemId, traits) -> {
|
||||
Registries.ITEM.getOrEmpty(itemId).ifPresent(item -> {
|
||||
|
@ -228,10 +228,7 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
|
|||
}
|
||||
|
||||
public static Stream<Item> getItems(Trait trait) {
|
||||
return REGISTRY.entrySet().stream()
|
||||
.filter(e -> e.getValue().get(trait) > 0)
|
||||
.map(Map.Entry::getKey)
|
||||
.flatMap(id -> Registries.ITEM.getOrEmpty(id).stream());
|
||||
return ITEMS.getOrDefault(trait, List.of()).stream();
|
||||
}
|
||||
|
||||
public static Optional<SpellTraits> getEmbeddedTraits(ItemStack stack) {
|
||||
|
|
|
@ -5,7 +5,9 @@ import java.io.InputStreamReader;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
@ -26,7 +28,11 @@ import net.minecraft.resource.SinglePreparationResourceReloader;
|
|||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.JsonHelper;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemConvertible;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
|
||||
public class TraitLoader extends SinglePreparationResourceReloader<Multimap<Identifier, TraitLoader.TraitStream>> implements IdentifiableResourceReloadListener {
|
||||
private static final Identifier ID = Unicopia.id("data/traits");
|
||||
|
@ -77,9 +83,24 @@ public class TraitLoader extends SinglePreparationResourceReloader<Multimap<Iden
|
|||
@Override
|
||||
protected void apply(Multimap<Identifier, TraitStream> prepared, ResourceManager manager, Profiler profiler) {
|
||||
profiler.startTick();
|
||||
SpellTraits.load(prepared.values().stream()
|
||||
|
||||
Set<Map.Entry<TraitStream.Key, SpellTraits>> newRegistry = prepared.values().stream()
|
||||
.flatMap(TraitStream::entries)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, SpellTraits::union)));
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, SpellTraits::union))
|
||||
.entrySet();
|
||||
|
||||
SpellTraits.load(Registries.ITEM.getEntrySet().stream()
|
||||
.map(entry -> Map.entry(
|
||||
entry.getKey().getValue(),
|
||||
newRegistry.stream()
|
||||
.filter(p -> p.getKey().test(entry.getValue()))
|
||||
.map(Map.Entry::getValue)
|
||||
.reduce(SpellTraits::union)
|
||||
.orElse(SpellTraits.EMPTY)
|
||||
))
|
||||
.filter(entry -> !entry.getValue().isEmpty())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
|
||||
profiler.endTick();
|
||||
}
|
||||
|
||||
|
@ -88,14 +109,14 @@ public class TraitLoader extends SinglePreparationResourceReloader<Multimap<Iden
|
|||
|
||||
boolean replace();
|
||||
|
||||
Stream<Map.Entry<Identifier, SpellTraits>> entries();
|
||||
Stream<Map.Entry<Key, SpellTraits>> entries();
|
||||
|
||||
static TraitStream of(Identifier id, String pack, JsonObject json) {
|
||||
|
||||
if (json.has("items") && json.get("items").isJsonObject()) {
|
||||
return new TraitMap(JsonHelper.getBoolean(json, "replace", false),
|
||||
Resources.GSON.getAdapter(TYPE).fromJsonTree(json.get("items")).entrySet().stream().collect(Collectors.toMap(
|
||||
a -> Identifier.tryParse(a.getKey()),
|
||||
a -> Key.of(a.getKey()),
|
||||
a -> SpellTraits.fromString(a.getValue()).orElse(SpellTraits.EMPTY)
|
||||
))
|
||||
);
|
||||
|
@ -106,23 +127,16 @@ public class TraitLoader extends SinglePreparationResourceReloader<Multimap<Iden
|
|||
SpellTraits.fromString(JsonHelper.getString(json, "traits")).orElse(SpellTraits.EMPTY),
|
||||
StreamSupport.stream(JsonHelper.getArray(json, "items").spliterator(), false)
|
||||
.map(JsonElement::getAsString)
|
||||
.map(Identifier::tryParse)
|
||||
.filter(item -> {
|
||||
if (item == null || !Registries.ITEM.containsId(item)) {
|
||||
Unicopia.LOGGER.warn("Skipping unknown item {} in {}:{}", item, pack, id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(Key::of)
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
|
||||
record TraitMap (
|
||||
boolean replace,
|
||||
Map<Identifier, SpellTraits> items) implements TraitStream {
|
||||
Map<Key, SpellTraits> items) implements TraitStream {
|
||||
@Override
|
||||
public Stream<Entry<Identifier, SpellTraits>> entries() {
|
||||
public Stream<Entry<Key, SpellTraits>> entries() {
|
||||
return items.entrySet().stream();
|
||||
}
|
||||
}
|
||||
|
@ -130,12 +144,32 @@ public class TraitLoader extends SinglePreparationResourceReloader<Multimap<Iden
|
|||
record TraitSet (
|
||||
boolean replace,
|
||||
SpellTraits traits,
|
||||
Set<Identifier> items) implements TraitStream {
|
||||
Set<Key> items) implements TraitStream {
|
||||
@Override
|
||||
public Stream<Entry<Identifier, SpellTraits>> entries() {
|
||||
public Stream<Entry<Key, SpellTraits>> entries() {
|
||||
return items().stream().map(item -> Map.entry(item, traits()));
|
||||
}
|
||||
}
|
||||
|
||||
interface Key extends Predicate<ItemConvertible> {
|
||||
static Key of(String s) {
|
||||
return s.startsWith("#") ? new Tag(TagKey.of(RegistryKeys.ITEM, Identifier.tryParse(s.substring(1)))) : new Id(Identifier.tryParse(s));
|
||||
}
|
||||
record Tag(TagKey<Item> tag) implements Key {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean test(ItemConvertible item) {
|
||||
return item.asItem().getRegistryEntry().isIn(tag);
|
||||
}
|
||||
}
|
||||
|
||||
record Id(Identifier id) implements Key {
|
||||
@Override
|
||||
public boolean test(ItemConvertible item) {
|
||||
return Objects.equals(id, Registries.ITEM.getId(item.asItem()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ public interface UCriteria {
|
|||
CustomEventCriterion.Trigger POWER_UP_HEART = CUSTOM_EVENT.createTrigger("power_up_heart");
|
||||
CustomEventCriterion.Trigger SPLIT_SEA = CUSTOM_EVENT.createTrigger("split_sea");
|
||||
CustomEventCriterion.Trigger RIDE_BALLOON = CUSTOM_EVENT.createTrigger("ride_balloon");
|
||||
CustomEventCriterion.Trigger TELEPORT_ABOVE_WORLD = CUSTOM_EVENT.createTrigger("teleport_above_world");
|
||||
|
||||
static void bootstrap() { }
|
||||
}
|
||||
|
|
|
@ -41,6 +41,10 @@ public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock
|
|||
|
||||
@Override
|
||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
|
||||
if (state.get(PERSISTENT)) {
|
||||
return state;
|
||||
}
|
||||
|
||||
if (world instanceof ServerWorld sw) {
|
||||
ZapAppleStageStore store = ZapAppleStageStore.get(sw);
|
||||
ZapAppleStageStore.Stage currentStage = store.getStage();
|
||||
|
@ -56,8 +60,10 @@ public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock
|
|||
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
super.scheduledTick(state, world, pos, random);
|
||||
tryAdvanceStage(state, world, pos, random);
|
||||
if (!state.get(PERSISTENT)) {
|
||||
world.scheduleBlockTick(pos, this, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryAdvanceStage(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
if (state.get(PERSISTENT)) {
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package com.minelittlepony.unicopia.block;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.EquineContext;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockSetType;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.DoorBlock;
|
||||
import net.minecraft.block.enums.DoubleBlockHalf;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.event.GameEvent;
|
||||
|
||||
public class CrystalDoorBlock extends DoorBlock {
|
||||
|
||||
public CrystalDoorBlock(Settings settings, BlockSetType blockSet) {
|
||||
super(settings, blockSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void neighborUpdate(BlockState state, World world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
|
||||
boolean powered = world.isReceivingRedstonePower(pos) || world.isReceivingRedstonePower(pos.offset(state.get(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN));
|
||||
if (!getDefaultState().isOf(sourceBlock) && powered != state.get(POWERED)) {
|
||||
if (powered) {
|
||||
state = state.cycle(OPEN);
|
||||
playOpenCloseSound(null, world, pos, state.get(OPEN));
|
||||
world.emitGameEvent(null, state.get(OPEN) ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
|
||||
}
|
||||
|
||||
world.setBlockState(pos, state.with(POWERED, powered), Block.NOTIFY_LISTENERS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
if (!EquineContext.of(player).getCompositeRace().any(Race::canCast)) {
|
||||
|
||||
if (!player.getStackInHand(hand).isOf(UItems.MEADOWBROOKS_STAFF)) {
|
||||
playOpenCloseSound(player, world, pos, false);
|
||||
return ActionResult.FAIL;
|
||||
} else {
|
||||
world.playSound(player, pos, USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, SoundCategory.BLOCKS, 1, world.getRandom().nextFloat() * 0.1F + 0.9F);
|
||||
}
|
||||
}
|
||||
return super.onUse(state, world, pos, player, hand, hit);
|
||||
}
|
||||
|
||||
private void playOpenCloseSound(@Nullable Entity entity, World world, BlockPos pos, boolean open) {
|
||||
world.playSound(entity, pos, open ? getBlockSetType().doorOpen() : getBlockSetType().doorClose(), SoundCategory.BLOCKS, 1, world.getRandom().nextFloat() * 0.1f + 0.9f);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
package com.minelittlepony.unicopia.block;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.item.BedsheetsItem;
|
||||
import com.minelittlepony.unicopia.util.VoxelShapeUtil;
|
||||
|
||||
import net.minecraft.block.BedBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BedBlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.block.enums.BedPart;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.DyeColor;
|
||||
import net.minecraft.util.StringIdentifiable;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class FancyBedBlock extends BedBlock {
|
||||
private static final List<Function<Direction, VoxelShape>> SHAPES = List.of(
|
||||
VoxelShapeUtil.rotator(VoxelShapes.union(
|
||||
createCuboidShape(0, 3, 1, 16, 9, 16),
|
||||
createCuboidShape(-0.5, 0, 1, 1.5, 13, 4),
|
||||
createCuboidShape(14.5, 0, 1, 16.5, 13, 4),
|
||||
createCuboidShape(1.5, 1, 0, 14.5, 16, 3)
|
||||
)),
|
||||
VoxelShapeUtil.rotator(VoxelShapes.union(
|
||||
createCuboidShape(0, 3, 0, 16, 9, 16),
|
||||
createCuboidShape(-0.5, 0, -1, 2.5, 10, 2),
|
||||
createCuboidShape(13.5, 0, -1, 16.5, 10, 2),
|
||||
createCuboidShape(1.5, 1, -2, 14.5, 12, 1)
|
||||
))
|
||||
);
|
||||
|
||||
private final String base;
|
||||
|
||||
public FancyBedBlock(String base, Settings settings) {
|
||||
super(DyeColor.WHITE, settings);
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return SHAPES.get(state.get(PART).ordinal()).apply(BedBlock.getOppositePartDirection(state));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new Tile(pos, state);
|
||||
}
|
||||
|
||||
public static void setBedPattern(World world, BlockPos pos, SheetPattern pattern) {
|
||||
world.getBlockEntity(pos, UBlockEntities.FANCY_BED).ifPresent(tile -> {
|
||||
ItemStack stack = BedsheetsItem.forPattern(tile.getPattern()).getDefaultStack();
|
||||
if (!stack.isEmpty()) {
|
||||
Block.dropStack(world, pos, stack);
|
||||
}
|
||||
tile.setPattern(pattern);
|
||||
BlockState state = tile.getCachedState();
|
||||
BlockPos other = pos.offset(getDirectionTowardsOtherPart(state.get(PART), state.get(FACING)));
|
||||
world.getBlockEntity(other, UBlockEntities.FANCY_BED).ifPresent(tile2 -> {
|
||||
tile2.setPattern(pattern);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static Direction getDirectionTowardsOtherPart(BedPart part, Direction direction) {
|
||||
return part == BedPart.FOOT ? direction : direction.getOpposite();
|
||||
}
|
||||
|
||||
public static class Tile extends BedBlockEntity {
|
||||
private SheetPattern pattern = SheetPattern.NONE;
|
||||
|
||||
public Tile(BlockPos pos, BlockState state) {
|
||||
super(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntityType<?> getType() {
|
||||
return UBlockEntities.FANCY_BED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNbt(NbtCompound nbt) {
|
||||
pattern = SheetPattern.byId(nbt.getString("pattern"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeNbt(NbtCompound nbt) {
|
||||
nbt.putString("pattern", pattern.asString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound toInitialChunkDataNbt() {
|
||||
return createNbt();
|
||||
}
|
||||
|
||||
public String getBase() {
|
||||
return ((FancyBedBlock)getCachedState().getBlock()).base;
|
||||
}
|
||||
|
||||
public void setPattern(SheetPattern pattern) {
|
||||
this.pattern = pattern;
|
||||
markDirty();
|
||||
if (world instanceof ServerWorld sw) {
|
||||
sw.getChunkManager().markForUpdate(getPos());
|
||||
}
|
||||
}
|
||||
|
||||
public SheetPattern getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SheetPattern implements StringIdentifiable {
|
||||
NONE(DyeColor.WHITE),
|
||||
LIGHT_GRAY(DyeColor.LIGHT_GRAY),
|
||||
GRAY(DyeColor.GRAY),
|
||||
BLACK(DyeColor.BLACK),
|
||||
BROWN(DyeColor.BROWN),
|
||||
RED(DyeColor.RED),
|
||||
ORANGE(DyeColor.ORANGE),
|
||||
YELLOW(DyeColor.YELLOW),
|
||||
LIME(DyeColor.LIME),
|
||||
GREEN(DyeColor.GREEN),
|
||||
CYAN(DyeColor.CYAN),
|
||||
LIGHT_BLUE(DyeColor.LIGHT_BLUE),
|
||||
BLUE(DyeColor.BLUE),
|
||||
PURPLE(DyeColor.PURPLE),
|
||||
MAGENTA(DyeColor.MAGENTA),
|
||||
PINK(DyeColor.PINK),
|
||||
|
||||
APPLE(null),
|
||||
BARS(null),
|
||||
CHECKER(null),
|
||||
KELP(null),
|
||||
RAINBOW(null),
|
||||
RAINBOW_BPW(null),
|
||||
RAINBOW_BPY(null),
|
||||
RAINBOW_PBG(null),
|
||||
RAINBOW_PWR(null);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static final EnumCodec<SheetPattern> CODEC = StringIdentifiable.createCodec(SheetPattern::values);
|
||||
|
||||
private final String name = name().toLowerCase(Locale.ROOT);
|
||||
@Nullable
|
||||
private final DyeColor color;
|
||||
|
||||
SheetPattern(@Nullable DyeColor color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DyeColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static SheetPattern byId(String id) {
|
||||
return CODEC.byId(id, NONE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.minelittlepony.unicopia.block;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.Waterloggable;
|
||||
import net.minecraft.entity.ai.pathing.NavigationType;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.registry.tag.FluidTags;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.BooleanProperty;
|
||||
import net.minecraft.state.property.IntProperty;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
|
||||
public class ShellsBlock extends Block implements Waterloggable {
|
||||
public static final BooleanProperty WATERLOGGED = Properties.WATERLOGGED;
|
||||
public static final IntProperty COUNT = IntProperty.of("count", 1, 4);
|
||||
|
||||
private static final VoxelShape SHAPE = Block.createCuboidShape(0, 0, 0, 16, 1, 16);
|
||||
|
||||
public ShellsBlock(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return SHAPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder);
|
||||
builder.add(COUNT, WATERLOGGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return getDefaultState().with(WATERLOGGED, ctx.getWorld().getFluidState(ctx.getBlockPos()).getFluid() == Fluids.WATER);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FluidState getFluidState(BlockState state) {
|
||||
if (state.get(WATERLOGGED).booleanValue()) {
|
||||
return Fluids.WATER.getStill(false);
|
||||
}
|
||||
return super.getFluidState(state);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
|
||||
if (state.get(WATERLOGGED).booleanValue()) {
|
||||
world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
|
||||
}
|
||||
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
|
||||
return (type == NavigationType.WATER) == world.getFluidState(pos).isIn(FluidTags.WATER);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
package com.minelittlepony.unicopia.block;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
|
@ -14,11 +17,14 @@ import net.minecraft.entity.mob.SlimeEntity;
|
|||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.particle.BlockStateParticleEffect;
|
||||
import net.minecraft.particle.DustParticleEffect;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.BooleanProperty;
|
||||
import net.minecraft.state.property.EnumProperty;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.StringIdentifiable;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Box;
|
||||
|
@ -34,24 +40,29 @@ import net.minecraft.world.WorldAccess;
|
|||
import net.minecraft.world.WorldView;
|
||||
|
||||
public class SlimePustuleBlock extends Block {
|
||||
static final EnumProperty<Shape> SHAPE = EnumProperty.of("shape", Shape.class);
|
||||
static final VoxelShape SHAFT_SHAPE = Block.createCuboidShape(7.5, 0, 7.5, 8.5, 16, 8.5);
|
||||
static final VoxelShape DRIP_SHAPE = VoxelShapes.union(
|
||||
private static final EnumProperty<Shape> SHAPE = EnumProperty.of("shape", Shape.class);
|
||||
private static final BooleanProperty POWERED = Properties.POWERED;
|
||||
private static final Direction[] DIRECTIONS = Arrays.stream(Direction.values())
|
||||
.filter(direction -> direction != Direction.UP)
|
||||
.toArray(Direction[]::new);
|
||||
private static final VoxelShape SHAFT_SHAPE = Block.createCuboidShape(7.5, 0, 7.5, 8.5, 16, 8.5);
|
||||
private static final VoxelShape DRIP_SHAPE = VoxelShapes.union(
|
||||
Block.createCuboidShape(7, 10, 7, 9, 16, 9),
|
||||
Block.createCuboidShape(3, 15, 4, 9, 16, 10),
|
||||
Block.createCuboidShape(7, 15, 7, 12, 16, 12)
|
||||
);
|
||||
static final VoxelShape BULB_SHAPE = VoxelShapes.union(
|
||||
private static final VoxelShape BULB_SHAPE = VoxelShapes.union(
|
||||
Block.createCuboidShape(4, 1, 4, 12, 10, 12),
|
||||
Block.createCuboidShape(5, 10, 5, 11, 13, 11),
|
||||
Block.createCuboidShape(6, 13, 6, 10, 15, 10),
|
||||
Block.createCuboidShape(7, 13, 7, 9, 20, 9)
|
||||
);
|
||||
static final VoxelShape CAP_SHAPE = VoxelShapes.union(SHAFT_SHAPE, DRIP_SHAPE);
|
||||
private static final VoxelShape CAP_SHAPE = VoxelShapes.union(SHAFT_SHAPE, DRIP_SHAPE);
|
||||
private static final Vector3f DUST_COLOR = new Vector3f(1, 0.2F, 0.1F);
|
||||
|
||||
public SlimePustuleBlock(Settings settings) {
|
||||
super(settings.ticksRandomly());
|
||||
setDefaultState(getDefaultState().with(SHAPE, Shape.DRIP));
|
||||
setDefaultState(getDefaultState().with(SHAPE, Shape.DRIP).with(POWERED, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,6 +79,17 @@ public class SlimePustuleBlock extends Block {
|
|||
public void randomDisplayTick(BlockState state, World world, BlockPos pos, Random random) {
|
||||
super.randomDisplayTick(state, world, pos, random);
|
||||
|
||||
if (state.get(POWERED)) {
|
||||
|
||||
VoxelShape shape = state.getCullingShape(world, pos);
|
||||
float x = (float)MathHelper.lerp(random.nextFloat(), shape.getMin(Axis.X), shape.getMax(Axis.X));
|
||||
float z = (float)MathHelper.lerp(random.nextFloat(), shape.getMin(Axis.Z), shape.getMax(Axis.Z));
|
||||
world.addParticle(new DustParticleEffect(DUST_COLOR, 1),
|
||||
pos.getX() + x,
|
||||
pos.getY() + random.nextDouble(),
|
||||
pos.getZ() + z, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (random.nextInt(15) == 0) {
|
||||
VoxelShape shape = state.getCullingShape(world, pos);
|
||||
float x = (float)MathHelper.lerp(random.nextFloat(), shape.getMin(Axis.X), shape.getMax(Axis.X));
|
||||
|
@ -82,7 +104,7 @@ public class SlimePustuleBlock extends Block {
|
|||
@Deprecated
|
||||
@Override
|
||||
public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
if (state.get(SHAPE) == Shape.POD) {
|
||||
if (state.get(SHAPE) == Shape.POD && random.nextInt(130) == 0) {
|
||||
SlimeEntity slime = EntityType.SLIME.create(world);
|
||||
slime.setSize(1, true);
|
||||
slime.setPosition(pos.toCenterPos());
|
||||
|
@ -132,7 +154,7 @@ public class SlimePustuleBlock extends Block {
|
|||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
builder.add(SHAPE);
|
||||
builder.add(SHAPE, POWERED);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
@ -154,20 +176,66 @@ public class SlimePustuleBlock extends Block {
|
|||
Shape currentShape = state.get(SHAPE);
|
||||
|
||||
if (direction == Direction.DOWN && (currentShape == Shape.CAP || currentShape == Shape.STRING)) {
|
||||
return state;
|
||||
return state.with(POWERED, getReceivedRedstonePower(world, pos) > 0);
|
||||
}
|
||||
|
||||
Shape shape = determineShape(world, pos);
|
||||
return state.with(SHAPE, shape);
|
||||
return state.with(SHAPE, shape).with(POWERED, getReceivedRedstonePower(world, pos) > 0);
|
||||
}
|
||||
|
||||
return state;
|
||||
return state.with(POWERED, getReceivedRedstonePower(world, pos) > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
Shape shape = determineShape(ctx.getWorld(), ctx.getBlockPos());
|
||||
return super.getPlacementState(ctx).with(SHAPE, shape == Shape.STRING ? Shape.POD : shape);
|
||||
return super.getPlacementState(ctx)
|
||||
.with(SHAPE, shape == Shape.STRING ? Shape.POD : shape)
|
||||
.with(POWERED, getReceivedRedstonePower(ctx.getWorld(), ctx.getBlockPos()) > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
if (state.isOf(this) && newState.isOf(this) && state.get(POWERED) != newState.get(POWERED)) {
|
||||
world.updateNeighbor(pos.up(), this, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return state.get(POWERED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public int getWeakRedstonePower(BlockState state, BlockView world, BlockPos pos, Direction direction) {
|
||||
if (direction == Direction.DOWN && emitsRedstonePower(state)) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public int getStrongRedstonePower(BlockState state, BlockView world, BlockPos pos, Direction direction) {
|
||||
if (direction == Direction.DOWN && emitsRedstonePower(state)) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int getReceivedRedstonePower(BlockView world, BlockPos pos) {
|
||||
int power = 0;
|
||||
for (Direction direction : DIRECTIONS) {
|
||||
power = Math.max(power, world.getBlockState(pos.offset(direction)).getStrongRedstonePower(world, pos, direction));
|
||||
if (power >= 15) {
|
||||
return Math.min(15, power);
|
||||
}
|
||||
}
|
||||
return Math.min(15, power);
|
||||
}
|
||||
|
||||
private Shape determineShape(WorldAccess world, BlockPos pos) {
|
||||
|
|
|
@ -11,8 +11,8 @@ import net.minecraft.world.WorldAccess;
|
|||
|
||||
public class StableDoorBlock extends DoorBlock {
|
||||
|
||||
public StableDoorBlock(Settings settings) {
|
||||
super(settings, BlockSetType.OAK);
|
||||
public StableDoorBlock(Settings settings, BlockSetType blockSet) {
|
||||
super(settings, blockSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -21,6 +21,12 @@ public class StableDoorBlock extends DoorBlock {
|
|||
|
||||
if (direction.getAxis() == Direction.Axis.Y && half == DoubleBlockHalf.LOWER == (direction == Direction.UP)) {
|
||||
if (neighborState.isOf(this) && neighborState.get(HALF) != half) {
|
||||
state = state
|
||||
.with(FACING, neighborState.get(FACING))
|
||||
.with(HINGE, neighborState.get(HINGE));
|
||||
if (half == DoubleBlockHalf.UPPER && direction == Direction.DOWN && !state.get(POWERED)) {
|
||||
state = state.with(OPEN, neighborState.get(OPEN));
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
|
|
151
src/main/java/com/minelittlepony/unicopia/block/ThornBlock.java
Normal file
151
src/main/java/com/minelittlepony/unicopia/block/ThornBlock.java
Normal file
|
@ -0,0 +1,151 @@
|
|||
package com.minelittlepony.unicopia.block;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Supplier;
|
||||
import com.minelittlepony.unicopia.ability.EarthPonyGrowAbility;
|
||||
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||
import com.minelittlepony.unicopia.util.VecHelper;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ConnectingBlock;
|
||||
import net.minecraft.block.Fertilizable;
|
||||
import net.minecraft.entity.SpawnReason;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.registry.tag.BlockTags;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.BooleanProperty;
|
||||
import net.minecraft.state.property.DirectionProperty;
|
||||
import net.minecraft.state.property.IntProperty;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.state.property.Property;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import net.minecraft.world.WorldView;
|
||||
|
||||
public class ThornBlock extends ConnectingBlock implements EarthPonyGrowAbility.Growable, Fertilizable {
|
||||
static final Collection<BooleanProperty> PROPERTIES = FACING_PROPERTIES.values();
|
||||
static final DirectionProperty FACING = Properties.FACING;
|
||||
static final int MAX_DISTANCE = 25;
|
||||
static final int MAX_AGE = 4;
|
||||
static final IntProperty DISTANCE = IntProperty.of("distance", 0, MAX_DISTANCE);
|
||||
static final IntProperty AGE = IntProperty.of("age", 0, MAX_AGE);
|
||||
|
||||
private final Supplier<Block> bud;
|
||||
|
||||
public ThornBlock(Settings settings, Supplier<Block> bud) {
|
||||
super(0.125F, settings);
|
||||
this.bud = bud;
|
||||
PROPERTIES.forEach(property -> setDefaultState(getDefaultState().with(property, false)));
|
||||
setDefaultState(getDefaultState()
|
||||
.with(FACING, Direction.DOWN)
|
||||
.with(DISTANCE, 0)
|
||||
.with(AGE, 0)
|
||||
.with(DOWN, true)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
builder.add(PROPERTIES.toArray(Property[]::new));
|
||||
builder.add(FACING, DISTANCE, AGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
if (state.get(AGE) == MAX_AGE
|
||||
&& random.nextInt(1200) == 0
|
||||
&& world.isPlayerInRange(pos.getX(), pos.getY(), pos.getZ(), 3)) {
|
||||
UEntities.LOOT_BUG.spawn(world, pos, SpawnReason.NATURAL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
if (!state.canPlaceAt(world, pos)) {
|
||||
world.breakBlock(pos, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomDisplayTick(BlockState state, World world, BlockPos pos, Random random) {
|
||||
if (state.get(AGE) == MAX_AGE && world.isPlayerInRange(pos.getX(), pos.getY(), pos.getZ(), 3)) {
|
||||
Vec3d particlePos = pos.toCenterPos().add(VecHelper.supply(() -> random.nextTriangular(0, 0.5)));
|
||||
world.addImportantParticle(ParticleTypes.ASH, particlePos.x, particlePos.y, particlePos.z, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
|
||||
if (direction == state.get(FACING) && !state.canPlaceAt(world, pos)) {
|
||||
world.scheduleBlockTick(pos, this, 1);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
|
||||
Direction facing = state.get(FACING);
|
||||
BlockState neighborState = world.getBlockState(pos.offset(facing));
|
||||
return (facing == Direction.DOWN && state.get(DISTANCE) == 0 && neighborState.isIn(BlockTags.DIRT))
|
||||
|| neighborState.isOf(this)
|
||||
|| neighborState.isOf(bud.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean grow(World world, BlockState state, BlockPos pos) {
|
||||
if (state.get(DISTANCE) >= MAX_DISTANCE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
world.setBlockState(pos, state.with(AGE, Math.min(state.get(AGE) + 1, MAX_AGE)));
|
||||
return FACING_PROPERTIES.keySet().stream()
|
||||
.filter(direction -> isConnected(state, world.getBlockState(pos.offset(direction)), direction))
|
||||
.map(direction -> {
|
||||
BlockPos p = pos.offset(direction);
|
||||
BlockState s = world.getBlockState(p);
|
||||
if (s.isAir()) {
|
||||
// sprout a new branch for cut off nodes
|
||||
world.setBlockState(p, bud.get().getDefaultState()
|
||||
.with(FACING, direction.getOpposite())
|
||||
.with(DISTANCE, state.get(DISTANCE) + 1)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return ((EarthPonyGrowAbility.Growable)s.getBlock()).grow(world, s, p);
|
||||
}).reduce(false, Boolean::logicalOr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFertilizable(WorldView world, BlockPos pos, BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGrow(World world, Random random, BlockPos pos, BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grow(ServerWorld world, Random random, BlockPos pos, BlockState state) {
|
||||
grow(world, state, pos);
|
||||
}
|
||||
|
||||
private boolean isConnected(BlockState state, BlockState neighborState, Direction direction) {
|
||||
if (!state.get(FACING_PROPERTIES.get(direction))) {
|
||||
return false;
|
||||
}
|
||||
if (neighborState.isAir()) {
|
||||
return true;
|
||||
}
|
||||
return neighborState.getBlock() instanceof EarthPonyGrowAbility.Growable
|
||||
&& (neighborState.isOf(this) || neighborState.isOf(bud.get()))
|
||||
&& neighborState.get(FACING) == direction.getOpposite();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package com.minelittlepony.unicopia.block;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.ability.EarthPonyGrowAbility;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.Fertilizable;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.DirectionProperty;
|
||||
import net.minecraft.state.property.IntProperty;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import net.minecraft.world.WorldView;
|
||||
|
||||
public class ThornBudBlock extends Block implements EarthPonyGrowAbility.Growable, Fertilizable {
|
||||
static final DirectionProperty FACING = Properties.FACING;
|
||||
static final int MAX_DISTANCE = 25;
|
||||
static final IntProperty DISTANCE = IntProperty.of("distance", 0, MAX_DISTANCE);
|
||||
|
||||
private final BlockState branchState;
|
||||
|
||||
public ThornBudBlock(Settings settings, BlockState branchState) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(FACING, Direction.DOWN).with(DISTANCE, 0));
|
||||
this.branchState = branchState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
builder.add(FACING, DISTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
if (random.nextInt(50) == 0) {
|
||||
grow(world, state, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
|
||||
if (direction == state.get(FACING) && !(neighborState.isOf(this) || neighborState.isOf(branchState.getBlock()))) {
|
||||
return Blocks.AIR.getDefaultState();
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean grow(World world, BlockState state, BlockPos pos) {
|
||||
if (state.get(DISTANCE) >= MAX_DISTANCE) {
|
||||
return false;
|
||||
}
|
||||
return pickGrowthDirection(world, state, pos).map(randomDirection -> {
|
||||
BlockPos p = pos.offset(randomDirection);
|
||||
|
||||
if (!canReplace(world.getBlockState(p))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
world.playSound(null, pos, USounds.Vanilla.ITEM_BONE_MEAL_USE, SoundCategory.BLOCKS);
|
||||
world.setBlockState(pos, branchState
|
||||
.with(FACING, state.get(FACING))
|
||||
.with(ThornBlock.FACING_PROPERTIES.get(state.get(FACING)), true)
|
||||
.with(ThornBlock.FACING_PROPERTIES.get(randomDirection), true));
|
||||
world.setBlockState(p, getDefaultState()
|
||||
.with(FACING, randomDirection.getOpposite())
|
||||
.with(DISTANCE, state.get(DISTANCE) + 1)
|
||||
);
|
||||
return true;
|
||||
}).orElse(false);
|
||||
}
|
||||
|
||||
protected boolean canReplace(BlockState state) {
|
||||
return state.isReplaceable();
|
||||
}
|
||||
|
||||
private static Optional<Direction> pickGrowthDirection(World world, BlockState state, BlockPos pos) {
|
||||
Direction excluded = state.get(FACING);
|
||||
return Util.getRandomOrEmpty(ThornBlock.FACING_PROPERTIES.keySet().stream()
|
||||
.filter(direction -> direction != excluded)
|
||||
.flatMap(direction -> getByWeight(direction, excluded))
|
||||
.toList(), world.getRandom());
|
||||
}
|
||||
|
||||
private static Stream<Direction> getByWeight(Direction input, Direction excluded) {
|
||||
return Stream.generate(() -> input)
|
||||
.limit(input.getAxis() == excluded.getAxis() ? 6L : input.getAxis() == Direction.Axis.Y ? 1L : 3L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFertilizable(WorldView world, BlockPos pos, BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGrow(World world, Random random, BlockPos pos, BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grow(ServerWorld world, Random random, BlockPos pos, BlockState state) {
|
||||
grow(world, state, pos);
|
||||
}
|
||||
}
|
|
@ -1,16 +1,19 @@
|
|||
package com.minelittlepony.unicopia.block;
|
||||
|
||||
import com.minelittlepony.unicopia.block.cloud.CloudBedBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.CloudChestBlock;
|
||||
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.block.entity.BlockEntityType.Builder;
|
||||
import net.minecraft.block.entity.ChestBlockEntity;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.Registries;
|
||||
|
||||
public interface UBlockEntities {
|
||||
BlockEntityType<WeatherVaneBlock.WeatherVane> WEATHER_VANE = create("weather_vane", BlockEntityType.Builder.create(WeatherVaneBlock.WeatherVane::new, UBlocks.WEATHER_VANE));
|
||||
BlockEntityType<CloudBedBlock.Tile> CLOUD_BED = create("cloud_bed", BlockEntityType.Builder.create(CloudBedBlock.Tile::new, UBlocks.CLOUD_BED));
|
||||
BlockEntityType<CloudBedBlock.Tile> FANCY_BED = create("fancy_bed", BlockEntityType.Builder.create(CloudBedBlock.Tile::new, UBlocks.CLOTH_BED, UBlocks.CLOUD_BED));
|
||||
BlockEntityType<ChestBlockEntity> CLOUD_CHEST = create("cloud_chest", BlockEntityType.Builder.create(CloudChestBlock.TileData::new, UBlocks.CLOUD_CHEST));
|
||||
|
||||
static <T extends BlockEntity> BlockEntityType<T> create(String id, Builder<T> builder) {
|
||||
return Registry.register(Registries.BLOCK_ENTITY_TYPE, id, builder.build(null));
|
||||
|
|
|
@ -10,10 +10,16 @@ import com.minelittlepony.unicopia.block.cloud.CloudSlabBlock;
|
|||
import com.minelittlepony.unicopia.block.cloud.CloudStairsBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.CompactedCloudBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.NaturalCloudBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.OrientedCloudBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.PoreousCloudStairsBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.ShapingBenchBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.CloudBedBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.CloudBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.CloudChestBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.CloudDoorBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.CloudLike;
|
||||
import com.minelittlepony.unicopia.block.cloud.SoggyCloudBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.SoggyCloudSlabBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.SoggyCloudStairsBlock;
|
||||
import com.minelittlepony.unicopia.block.cloud.UnstableCloudBlock;
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
import com.minelittlepony.unicopia.item.cloud.CloudBlockItem;
|
||||
|
@ -130,6 +136,9 @@ public interface UBlocks {
|
|||
SegmentedCropBlock OATS_STEM = register("oats_stem", OATS.createNext(5));
|
||||
SegmentedCropBlock OATS_CROWN = register("oats_crown", OATS_STEM.createNext(5));
|
||||
|
||||
Block PLUNDER_VINE = register("plunder_vine", new ThornBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).ticksRandomly().sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY), () -> UBlocks.PLUNDER_VINE_BUD));
|
||||
Block PLUNDER_VINE_BUD = register("plunder_vine_bud", new ThornBudBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).nonOpaque().ticksRandomly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY), PLUNDER_VINE.getDefaultState()));
|
||||
|
||||
Block CHITIN = register("chitin", new SnowyBlock(Settings.create().mapColor(MapColor.PALE_PURPLE).hardness(5).requiresTool().ticksRandomly().sounds(BlockSoundGroup.CORAL)), ItemGroups.NATURAL);
|
||||
Block SURFACE_CHITIN = register("surface_chitin", new GrowableBlock(Settings.copy(CHITIN), () -> CHITIN), ItemGroups.NATURAL);
|
||||
Block CHISELLED_CHITIN = register("chiselled_chitin", new Block(Settings.create().mapColor(MapColor.PALE_PURPLE).hardness(5).requiresTool()), ItemGroups.BUILDING_BLOCKS);
|
||||
|
@ -141,27 +150,61 @@ public interface UBlocks {
|
|||
Block MYSTERIOUS_EGG = register("mysterious_egg", new PileBlock(Settings.copy(Blocks.SLIME_BLOCK), PileBlock.MYSTERIOUS_EGG_SHAPES), ItemGroups.NATURAL);
|
||||
Block SLIME_PUSTULE = register("slime_pustule", new SlimePustuleBlock(Settings.copy(Blocks.SLIME_BLOCK)), ItemGroups.NATURAL);
|
||||
|
||||
Block SHAPING_BENCH = register("shaping_bench", new ShapingBenchBlock(Settings.create().mapColor(MapColor.OFF_WHITE).hardness(0.3F).resistance(0).sounds(BlockSoundGroup.WOOL)), ItemGroups.FUNCTIONAL);
|
||||
Block CLOUD = register("cloud", new NaturalCloudBlock(Settings.create().mapColor(MapColor.OFF_WHITE).hardness(0.3F).resistance(0).sounds(BlockSoundGroup.WOOL), true,
|
||||
() -> UBlocks.SOGGY_CLOUD,
|
||||
() -> UBlocks.COMPACTED_CLOUD), ItemGroups.NATURAL);
|
||||
Block COMPACTED_CLOUD = register("compacted_cloud", new CompactedCloudBlock(CLOUD.getDefaultState()));
|
||||
Block CLOUD_SLAB = register("cloud_slab", new CloudSlabBlock(Settings.copy(CLOUD), true, () -> UBlocks.SOGGY_CLOUD_SLAB), ItemGroups.NATURAL);
|
||||
Block CLOUD_STAIRS = register("cloud_stairs", new CloudStairsBlock(CLOUD.getDefaultState(), Settings.copy(CLOUD), () -> UBlocks.SOGGY_CLOUD_STAIRS), ItemGroups.NATURAL);
|
||||
Block COMPACTED_CLOUD = register("compacted_cloud", new CompactedCloudBlock(Settings.copy(CLOUD)));
|
||||
Block CLOUD_PLANKS = register("cloud_planks", new NaturalCloudBlock(Settings.copy(CLOUD).requiresTool(), false,
|
||||
PoreousCloudStairsBlock CLOUD_STAIRS = register("cloud_stairs", new PoreousCloudStairsBlock(CLOUD.getDefaultState(), Settings.copy(CLOUD), () -> UBlocks.SOGGY_CLOUD_STAIRS), ItemGroups.NATURAL);
|
||||
|
||||
Block CLOUD_PLANKS = register("cloud_planks", new NaturalCloudBlock(Settings.copy(CLOUD).hardness(0.4F).requiresTool().solid(), false,
|
||||
null,
|
||||
() -> UBlocks.COMPACTED_CLOUD_PLANKS), ItemGroups.BUILDING_BLOCKS);
|
||||
Block CLOUD_PLANKS_SLAB = register("cloud_planks_slab", new CloudSlabBlock(Settings.copy(CLOUD_PLANKS), false, null), ItemGroups.BUILDING_BLOCKS);
|
||||
Block CLOUD_PLANKS_STAIRS = register("cloud_planks_stairs", new CloudStairsBlock(CLOUD_PLANKS.getDefaultState(), Settings.copy(CLOUD_PLANKS), null), ItemGroups.BUILDING_BLOCKS);
|
||||
Block COMPACTED_CLOUD_PLANKS = register("compacted_cloud_planks", new CompactedCloudBlock(Settings.copy(CLOUD_PLANKS)));
|
||||
Block COMPACTED_CLOUD_PLANKS = register("compacted_cloud_planks", new CompactedCloudBlock(CLOUD_PLANKS.getDefaultState()));
|
||||
Block CLOUD_PLANK_SLAB = register("cloud_plank_slab", new CloudSlabBlock(Settings.copy(CLOUD_PLANKS), false, null), ItemGroups.BUILDING_BLOCKS);
|
||||
Block CLOUD_PLANK_STAIRS = register("cloud_plank_stairs", new CloudStairsBlock(CLOUD_PLANKS.getDefaultState(), Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
||||
|
||||
Block CLOUD_BRICKS = register("cloud_bricks", new NaturalCloudBlock(Settings.copy(CLOUD).hardness(0.6F).requiresTool().solid(), false,
|
||||
null,
|
||||
() -> UBlocks.COMPACTED_CLOUD_BRICKS), ItemGroups.BUILDING_BLOCKS);
|
||||
Block COMPACTED_CLOUD_BRICKS = register("compacted_cloud_bricks", new CompactedCloudBlock(CLOUD_BRICKS.getDefaultState()));
|
||||
Block CLOUD_BRICK_SLAB = register("cloud_brick_slab", new CloudSlabBlock(Settings.copy(CLOUD_BRICKS), false, null), ItemGroups.BUILDING_BLOCKS);
|
||||
Block CLOUD_BRICK_STAIRS = register("cloud_brick_stairs", new CloudStairsBlock(CLOUD_BRICKS.getDefaultState(), Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
||||
|
||||
Block ETCHED_CLOUD = register("etched_cloud", new NaturalCloudBlock(Settings.copy(CLOUD_BRICKS), false,
|
||||
null,
|
||||
() -> UBlocks.COMPACTED_CLOUD_BRICKS), ItemGroups.BUILDING_BLOCKS);
|
||||
Block COMPACTED_ETCHED_CLOUD = register("compacted_etched_cloud", new CompactedCloudBlock(ETCHED_CLOUD.getDefaultState()));
|
||||
Block ETCHED_CLOUD_SLAB = register("etched_cloud_slab", new CloudSlabBlock(Settings.copy(ETCHED_CLOUD), false, null), ItemGroups.BUILDING_BLOCKS);
|
||||
Block ETCHED_CLOUD_STAIRS = register("etched_cloud_stairs", new CloudStairsBlock(ETCHED_CLOUD.getDefaultState(), Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
||||
|
||||
SoggyCloudBlock SOGGY_CLOUD = register("soggy_cloud", new SoggyCloudBlock(Settings.copy(CLOUD).hardness(0.7F), () -> UBlocks.CLOUD));
|
||||
SoggyCloudSlabBlock SOGGY_CLOUD_SLAB = register("soggy_cloud_slab", new SoggyCloudSlabBlock(Settings.copy(SOGGY_CLOUD), () -> UBlocks.CLOUD_SLAB));
|
||||
SoggyCloudStairsBlock SOGGY_CLOUD_STAIRS = register("soggy_cloud_stairs", new SoggyCloudStairsBlock(SOGGY_CLOUD.getDefaultState(), Settings.copy(CLOUD), () -> UBlocks.CLOUD_STAIRS));
|
||||
|
||||
Block DENSE_CLOUD = register("dense_cloud", new NaturalCloudBlock(Settings.create().mapColor(MapColor.GRAY).hardness(0.5F).resistance(0).sounds(BlockSoundGroup.WOOL).solid(), false,
|
||||
null,
|
||||
() -> UBlocks.COMPACTED_DENSE_CLOUD), ItemGroups.BUILDING_BLOCKS);
|
||||
Block COMPACTED_DENSE_CLOUD = register("compacted_dense_cloud", new CompactedCloudBlock(DENSE_CLOUD.getDefaultState()));
|
||||
Block DENSE_CLOUD_SLAB = register("dense_cloud_slab", new CloudSlabBlock(Settings.copy(DENSE_CLOUD), false, null), ItemGroups.BUILDING_BLOCKS);
|
||||
Block DENSE_CLOUD_STAIRS = register("dense_cloud_stairs", new CloudStairsBlock(DENSE_CLOUD.getDefaultState(), Settings.copy(DENSE_CLOUD)), ItemGroups.BUILDING_BLOCKS);
|
||||
|
||||
Block CARVED_CLOUD = register("carved_cloud", new OrientedCloudBlock(Settings.copy(CLOUD).hardness(0.4F).requiresTool().solid(), false), ItemGroups.BUILDING_BLOCKS);
|
||||
Block UNSTABLE_CLOUD = register("unstable_cloud", new UnstableCloudBlock(Settings.copy(CLOUD)), ItemGroups.NATURAL);
|
||||
SoggyCloudBlock SOGGY_CLOUD = register("soggy_cloud", new SoggyCloudBlock(Settings.copy(CLOUD), () -> UBlocks.CLOUD));
|
||||
SoggyCloudSlabBlock SOGGY_CLOUD_SLAB = register("soggy_cloud_slab", new SoggyCloudSlabBlock(Settings.copy(CLOUD), () -> UBlocks.CLOUD_SLAB));
|
||||
CloudStairsBlock SOGGY_CLOUD_STAIRS = register("soggy_cloud_stairs", new CloudStairsBlock(SOGGY_CLOUD.getDefaultState(), Settings.copy(CLOUD), null));
|
||||
Block DENSE_CLOUD = register("dense_cloud", new CloudBlock(Settings.create().mapColor(MapColor.GRAY).hardness(0.5F).resistance(0).sounds(BlockSoundGroup.WOOL), false), ItemGroups.NATURAL);
|
||||
Block DENSE_CLOUD_SLAB = register("dense_cloud_slab", new CloudSlabBlock(Settings.copy(DENSE_CLOUD), false, null), ItemGroups.NATURAL);
|
||||
Block DENSE_CLOUD_STAIRS = register("dense_cloud_stairs", new CloudStairsBlock(DENSE_CLOUD.getDefaultState(), Settings.copy(DENSE_CLOUD), null), ItemGroups.NATURAL);
|
||||
Block CLOUD_PILLAR = register("cloud_pillar", new CloudPillarBlock(Settings.create().mapColor(MapColor.GRAY).hardness(0.5F).resistance(0).sounds(BlockSoundGroup.WOOL)), ItemGroups.NATURAL);
|
||||
Block CLOUD_BED = register("cloud_bed", new CloudBedBlock(CLOUD.getDefaultState(), Settings.copy(Blocks.WHITE_BED).sounds(BlockSoundGroup.WOOL)));
|
||||
Block CLOUD_PILLAR = register("cloud_pillar", new CloudPillarBlock(Settings.create().mapColor(MapColor.GRAY).hardness(0.5F).resistance(0).sounds(BlockSoundGroup.WOOL).solid()), ItemGroups.NATURAL);
|
||||
Block CLOUD_CHEST = register("cloud_chest", new CloudChestBlock(Settings.copy(DENSE_CLOUD).instrument(Instrument.BASS).strength(2.5f), DENSE_CLOUD.getDefaultState()), ItemGroups.FUNCTIONAL);
|
||||
Block CLOTH_BED = register("cloth_bed", new FancyBedBlock("cloth", Settings.copy(Blocks.WHITE_BED).sounds(BlockSoundGroup.WOOD)));
|
||||
Block CLOUD_BED = register("cloud_bed", new CloudBedBlock("cloud", CLOUD.getDefaultState(), Settings.copy(Blocks.WHITE_BED).sounds(BlockSoundGroup.WOOL)));
|
||||
|
||||
Block CLAM_SHELL = register("clam_shell", new ShellsBlock(Settings.create().mapColor(MapColor.DULL_PINK).breakInstantly().nonOpaque()));
|
||||
Block SCALLOP_SHELL = register("scallop_shell", new ShellsBlock(Settings.create().mapColor(MapColor.DULL_PINK).breakInstantly().nonOpaque()));
|
||||
Block TURRET_SHELL = register("turret_shell", new ShellsBlock(Settings.create().mapColor(MapColor.DULL_PINK).breakInstantly().nonOpaque()));
|
||||
|
||||
Block STABLE_DOOR = register("stable_door", new StableDoorBlock(Settings.copy(Blocks.OAK_DOOR), BlockSetType.OAK), ItemGroups.FUNCTIONAL);
|
||||
Block DARK_OAK_DOOR = register("dark_oak_stable_door", new StableDoorBlock(Settings.copy(Blocks.OAK_DOOR), BlockSetType.OAK), ItemGroups.FUNCTIONAL);
|
||||
Block CRYSTAL_DOOR = register("crystal_door", new CrystalDoorBlock(Settings.copy(Blocks.IRON_DOOR), UWoodTypes.CRYSTAL), ItemGroups.FUNCTIONAL);
|
||||
Block CLOUD_DOOR = register("cloud_door", new CloudDoorBlock(Settings.copy(CLOUD), CLOUD.getDefaultState(), UWoodTypes.CLOUD), ItemGroups.FUNCTIONAL);
|
||||
|
||||
private static <T extends Block> T register(String name, T item) {
|
||||
return register(Unicopia.id(name), item);
|
||||
|
@ -172,9 +215,7 @@ public interface UBlocks {
|
|||
}
|
||||
|
||||
static <T extends Block> T register(Identifier id, T block, RegistryKey<ItemGroup> group) {
|
||||
ItemGroupRegistry.register(id,
|
||||
CloudBlock.isCloudBlock(block) ? new CloudBlockItem(block, new Item.Settings()) : new BlockItem(block, new Item.Settings()
|
||||
), group);
|
||||
ItemGroupRegistry.register(id, block instanceof CloudLike ? new CloudBlockItem(block, new Item.Settings()) : new BlockItem(block, new Item.Settings()), group);
|
||||
return register(id, block);
|
||||
}
|
||||
|
||||
|
@ -185,7 +226,7 @@ public interface UBlocks {
|
|||
if (block instanceof SaplingBlock || block instanceof SproutBlock || block instanceof FruitBlock || block instanceof CropBlock || block instanceof DoorBlock || block instanceof TrapdoorBlock) {
|
||||
TRANSLUCENT_BLOCKS.add(block);
|
||||
}
|
||||
if (CloudBlock.isCloudBlock(block) || block instanceof SlimePustuleBlock || block instanceof PileBlock) {
|
||||
if (block instanceof CloudLike || block instanceof SlimePustuleBlock || block instanceof PileBlock) {
|
||||
SEMI_TRANSPARENT_BLOCKS.add(block);
|
||||
}
|
||||
return Registry.register(Registries.BLOCK, id, block);
|
||||
|
@ -199,7 +240,7 @@ public interface UBlocks {
|
|||
StrippableBlockRegistry.register(PALM_LOG, STRIPPED_PALM_LOG);
|
||||
StrippableBlockRegistry.register(ZAP_WOOD, STRIPPED_ZAP_WOOD);
|
||||
StrippableBlockRegistry.register(PALM_WOOD, STRIPPED_PALM_WOOD);
|
||||
Collections.addAll(TRANSLUCENT_BLOCKS, WEATHER_VANE, CHITIN_SPIKES);
|
||||
Collections.addAll(TRANSLUCENT_BLOCKS, WEATHER_VANE, CHITIN_SPIKES, PLUNDER_VINE, PLUNDER_VINE_BUD, CLAM_SHELL, SCALLOP_SHELL, TURRET_SHELL);
|
||||
TintedBlock.REGISTRY.add(PALM_LEAVES);
|
||||
|
||||
FlammableBlockRegistry.getDefaultInstance().add(GREEN_APPLE_LEAVES, 30, 60);
|
||||
|
|
|
@ -1,25 +1,34 @@
|
|||
package com.minelittlepony.unicopia.block;
|
||||
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.terraformersmc.terraform.boat.api.TerraformBoatType;
|
||||
import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry;
|
||||
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.type.BlockSetTypeBuilder;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.type.WoodTypeBuilder;
|
||||
import net.minecraft.block.BlockSetType;
|
||||
import net.minecraft.block.WoodType;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.sound.BlockSoundGroup;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public interface UWoodTypes {
|
||||
WoodType PALM = register("palm");
|
||||
RegistryKey<TerraformBoatType> PALM_BOAT_TYPE = TerraformBoatTypeRegistry.createKey(Unicopia.id("palm"));
|
||||
|
||||
BlockSetType CLOUD = new BlockSetTypeBuilder()
|
||||
.soundGroup(BlockSoundGroup.WOOL)
|
||||
.doorCloseSound(USounds.Vanilla.BLOCK_WOOL_HIT)
|
||||
.doorOpenSound(USounds.Vanilla.BLOCK_WOOL_BREAK)
|
||||
.register(Unicopia.id("cloud"));
|
||||
BlockSetType CRYSTAL = BlockSetTypeBuilder.copyOf(BlockSetType.IRON)
|
||||
.soundGroup(BlockSoundGroup.AMETHYST_BLOCK)
|
||||
.openableByHand(true)
|
||||
.register(Unicopia.id("crystal"));
|
||||
|
||||
static WoodType register(String name) {
|
||||
Identifier id = Unicopia.id(name);
|
||||
return new WoodTypeBuilder().register(id, new BlockSetTypeBuilder().register(id));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.block;
|
|||
import com.minelittlepony.unicopia.server.world.ZapAppleStageStore;
|
||||
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.*;
|
||||
|
@ -13,12 +14,13 @@ public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock {
|
|||
public static final EnumProperty<ZapAppleStageStore.Stage> STAGE = EnumProperty.of("stage", ZapAppleStageStore.Stage.class);
|
||||
|
||||
ZapAppleLeavesBlock() {
|
||||
setDefaultState(getDefaultState().with(STAGE, ZapAppleStageStore.Stage.HIBERNATING));
|
||||
setDefaultState(getDefaultState().with(STAGE, ZapAppleStageStore.Stage.GREENING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) {
|
||||
if (oldState.isOf(state.getBlock())
|
||||
if (state.get(PERSISTENT)
|
||||
|| oldState.isOf(state.getBlock())
|
||||
|| oldState.isOf(UBlocks.ZAP_LEAVES)
|
||||
|| oldState.isOf(UBlocks.FLOWERING_ZAP_LEAVES)
|
||||
|| oldState.isOf(UBlocks.ZAP_LEAVES_PLACEHOLDER)
|
||||
|
@ -33,6 +35,11 @@ public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return super.getPlacementState(ctx).with(STAGE, ZapAppleStageStore.Stage.GREENING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ZapAppleStageStore.Stage getStage(BlockState state) {
|
||||
return state.get(STAGE);
|
||||
|
|
|
@ -3,20 +3,14 @@ package com.minelittlepony.unicopia.block.cloud;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.EquineContext;
|
||||
import com.minelittlepony.unicopia.block.UBlockEntities;
|
||||
|
||||
import net.minecraft.block.BedBlock;
|
||||
import com.minelittlepony.unicopia.block.FancyBedBlock;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BedBlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.ai.pathing.NavigationType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.DyeColor;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
@ -25,12 +19,12 @@ import net.minecraft.util.shape.VoxelShapes;
|
|||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CloudBedBlock extends BedBlock {
|
||||
public class CloudBedBlock extends FancyBedBlock implements CloudLike {
|
||||
private final BlockState baseState;
|
||||
private final CloudBlock baseBlock;
|
||||
|
||||
public CloudBedBlock(BlockState baseState, Settings settings) {
|
||||
super(DyeColor.WHITE, settings);
|
||||
public CloudBedBlock(String base, BlockState baseState, Settings settings) {
|
||||
super(base, settings);
|
||||
this.baseState = baseState;
|
||||
this.baseBlock = (CloudBlock)baseState.getBlock();
|
||||
}
|
||||
|
@ -83,20 +77,4 @@ public class CloudBedBlock extends BedBlock {
|
|||
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new Tile(pos, state);
|
||||
}
|
||||
|
||||
public static class Tile extends BedBlockEntity {
|
||||
public Tile(BlockPos pos, BlockState state) {
|
||||
super(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntityType<?> getType() {
|
||||
return UBlockEntities.CLOUD_BED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,11 +26,7 @@ import net.minecraft.world.EmptyBlockView;
|
|||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CloudBlock extends Block {
|
||||
public static boolean isCloudBlock(Block block) {
|
||||
return block instanceof CloudBlock || block instanceof CloudStairsBlock || block instanceof CloudBedBlock;
|
||||
}
|
||||
|
||||
public class CloudBlock extends Block implements CloudLike {
|
||||
protected final boolean meltable;
|
||||
|
||||
public CloudBlock(Settings settings, boolean meltable) {
|
||||
|
@ -59,7 +55,7 @@ public class CloudBlock extends Block {
|
|||
entity.handleFallDamage(fallDistance, 0, world.getDamageSources().fall());
|
||||
generateSurfaceParticles(world, state, pos, ShapeContext.absent(), 9);
|
||||
|
||||
if (fallDistance > 7) {
|
||||
if (!world.isClient && fallDistance > 7) {
|
||||
world.breakBlock(pos, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
package com.minelittlepony.unicopia.block.cloud;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.EquineContext;
|
||||
import com.minelittlepony.unicopia.block.UBlockEntities;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ChestBlock;
|
||||
import net.minecraft.block.DoubleBlockProperties;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.block.entity.ChestBlockEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.ai.pathing.NavigationType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.inventory.DoubleInventory;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.screen.GenericContainerScreenHandler;
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CloudChestBlock extends ChestBlock implements CloudLike {
|
||||
private final BlockState baseState;
|
||||
private final CloudBlock baseBlock;
|
||||
|
||||
private static final DoubleBlockProperties.PropertyRetriever<ChestBlockEntity, Optional<NamedScreenHandlerFactory>> NAME_RETRIEVER = new DoubleBlockProperties.PropertyRetriever<>(){
|
||||
@Override
|
||||
public Optional<NamedScreenHandlerFactory> getFromBoth(final ChestBlockEntity first, final ChestBlockEntity second) {
|
||||
final DoubleInventory inventory = new DoubleInventory(first, second);
|
||||
return Optional.of(new NamedScreenHandlerFactory(){
|
||||
@Override
|
||||
@Nullable
|
||||
public ScreenHandler createMenu(int i, PlayerInventory playerInventory, PlayerEntity player) {
|
||||
if (first.checkUnlocked(player) && second.checkUnlocked(player)) {
|
||||
first.checkLootInteraction(playerInventory.player);
|
||||
second.checkLootInteraction(playerInventory.player);
|
||||
return GenericContainerScreenHandler.createGeneric9x6(i, playerInventory, inventory);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Text getDisplayName() {
|
||||
if (first.hasCustomName()) {
|
||||
return first.getDisplayName();
|
||||
}
|
||||
if (second.hasCustomName()) {
|
||||
return second.getDisplayName();
|
||||
}
|
||||
return Text.translatable(first.getCachedState().getBlock().getTranslationKey() + ".double");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<NamedScreenHandlerFactory> getFrom(ChestBlockEntity chest) {
|
||||
return Optional.of(chest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<NamedScreenHandlerFactory> getFallback() {
|
||||
return Optional.empty();
|
||||
}
|
||||
};
|
||||
|
||||
public CloudChestBlock(Settings settings, BlockState baseState) {
|
||||
super(settings, () -> UBlockEntities.CLOUD_CHEST);
|
||||
this.baseState = baseState;
|
||||
this.baseBlock = (CloudBlock)baseState.getBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new TileData(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) {
|
||||
return getBlockEntitySource(state, world, pos, false).apply(NAME_RETRIEVER).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
if (!baseBlock.canInteract(baseState, world, pos, EquineContext.of(context))) {
|
||||
return VoxelShapes.empty();
|
||||
}
|
||||
return super.getOutlineShape(state, world, pos, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final VoxelShape getCullingShape(BlockState state, BlockView world, BlockPos pos) {
|
||||
return super.getOutlineShape(state, world, pos, ShapeContext.absent());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return this.collidable ? state.getOutlineShape(world, pos, context) : VoxelShapes.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final BlockState getPlacementState(ItemPlacementContext context) {
|
||||
if (!baseBlock.canInteract(baseState, context.getWorld(), context.getBlockPos(), EquineContext.of(context))) {
|
||||
return null;
|
||||
}
|
||||
return super.getPlacementState(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
if (!baseBlock.canInteract(baseState, world, pos, EquineContext.of(player))) {
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
return super.onUse(state, world, pos, player, hand, hit);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) {
|
||||
baseState.onEntityCollision(world, pos, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class TileData extends ChestBlockEntity {
|
||||
protected TileData(BlockEntityType<?> blockEntityType, BlockPos blockPos, BlockState blockState) {
|
||||
super(blockEntityType, blockPos, blockState);
|
||||
}
|
||||
|
||||
public TileData(BlockPos pos, BlockState state) {
|
||||
super(UBlockEntities.CLOUD_CHEST, pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Text getContainerName() {
|
||||
return getCachedState().getBlock().getName();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package com.minelittlepony.unicopia.block.cloud;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.EquineContext;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
|
||||
import net.minecraft.block.BlockSetType;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.DoorBlock;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CloudDoorBlock extends DoorBlock implements CloudLike {
|
||||
private final BlockState baseState;
|
||||
private final CloudBlock baseBlock;
|
||||
|
||||
public CloudDoorBlock(Settings settings, BlockState baseState, BlockSetType blockSet) {
|
||||
super(settings, blockSet);
|
||||
this.baseState = baseState;
|
||||
this.baseBlock = (CloudBlock)baseState.getBlock();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
if (canPassThrough(state, world, pos, EquineContext.of(context))) {
|
||||
return VoxelShapes.empty();
|
||||
}
|
||||
return super.getOutlineShape(state, world, pos, context);
|
||||
}
|
||||
|
||||
protected boolean canPassThrough(BlockState state, BlockView world, BlockPos pos, EquineContext context) {
|
||||
return context.getCompositeRace().any(Race::canUseEarth);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final VoxelShape getCullingShape(BlockState state, BlockView world, BlockPos pos) {
|
||||
return super.getOutlineShape(state, world, pos, ShapeContext.absent());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return this.collidable ? state.getOutlineShape(world, pos, context) : VoxelShapes.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final BlockState getPlacementState(ItemPlacementContext context) {
|
||||
if (!baseBlock.canInteract(baseState, context.getWorld(), context.getBlockPos(), EquineContext.of(context))) {
|
||||
return null;
|
||||
}
|
||||
return super.getPlacementState(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
if (!baseBlock.canInteract(baseState, world, pos, EquineContext.of(player))) {
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
return super.onUse(state, world, pos, player, hand, hit);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) {
|
||||
baseState.onEntityCollision(world, pos, entity);
|
||||
|
||||
EquineContext context = EquineContext.of(entity);
|
||||
|
||||
if (!baseBlock.canInteract(baseState, world, pos, context)) {
|
||||
entity.setVelocity(entity.getVelocity().multiply(0.5F, 1, 0.5F));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.minelittlepony.unicopia.block.cloud;
|
||||
|
||||
public interface CloudLike {
|
||||
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
package com.minelittlepony.unicopia.block.cloud;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.EquineContext;
|
||||
|
@ -18,15 +16,13 @@ import net.minecraft.util.shape.VoxelShapes;
|
|||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CloudStairsBlock extends StairsBlock implements Soakable {
|
||||
public class CloudStairsBlock extends StairsBlock implements CloudLike {
|
||||
|
||||
private final CloudBlock baseBlock;
|
||||
private final @Nullable Supplier<Soakable> soggyBlock;
|
||||
|
||||
public CloudStairsBlock(BlockState baseState, Settings settings, @Nullable Supplier<Soakable> soggyBlock) {
|
||||
public CloudStairsBlock(BlockState baseState, Settings settings) {
|
||||
super(baseState, settings);
|
||||
this.baseBlock = (CloudBlock)baseState.getBlock();
|
||||
this.soggyBlock = soggyBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,15 +88,4 @@ public class CloudStairsBlock extends StairsBlock implements Soakable {
|
|||
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getSoggyState(int moisture) {
|
||||
return soggyBlock == null ? (baseBlock instanceof Soakable s ? s.getSoggyState(moisture) : null) : soggyBlock.get().getSoggyState(moisture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMoisture(BlockState state) {
|
||||
return baseBlock instanceof Soakable s ? s.getMoisture(state) : 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import net.minecraft.state.StateManager;
|
|||
import net.minecraft.state.property.BooleanProperty;
|
||||
import net.minecraft.state.property.Property;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.BlockMirror;
|
||||
import net.minecraft.util.BlockRotation;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
|
@ -43,13 +45,21 @@ public class CompactedCloudBlock extends CloudBlock {
|
|||
);
|
||||
});
|
||||
|
||||
public CompactedCloudBlock(Settings settings) {
|
||||
super(settings, true);
|
||||
private final BlockState baseState;
|
||||
|
||||
public CompactedCloudBlock(BlockState baseState) {
|
||||
super(Settings.copy(baseState.getBlock()).dropsLike(baseState.getBlock()), true);
|
||||
this.baseState = baseState;
|
||||
PROPERTIES.forEach(property -> {
|
||||
setDefaultState(getDefaultState().with(property, true));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getPickStack(BlockView world, BlockPos pos, BlockState state) {
|
||||
return baseState.getBlock().getPickStack(world, pos, baseState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context, EquineContext equineContext) {
|
||||
return SHAPE_CACHE.apply(state);
|
||||
|
@ -76,4 +86,24 @@ public class CompactedCloudBlock extends CloudBlock {
|
|||
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState rotate(BlockState state, BlockRotation rotation) {
|
||||
return transform(state, rotation::rotate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState mirror(BlockState state, BlockMirror mirror) {
|
||||
return transform(state, mirror::apply);
|
||||
}
|
||||
|
||||
private BlockState transform(BlockState state, Function<Direction, Direction> transformation) {
|
||||
BlockState result = state;
|
||||
for (var property : FACING_PROPERTIES.entrySet()) {
|
||||
if (property.getKey().getAxis() != Direction.Axis.Y) {
|
||||
result = result.with(FACING_PROPERTIES.get(transformation.apply(property.getKey())), state.get(property.getValue()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package com.minelittlepony.unicopia.block.cloud;
|
||||
|
||||
import com.minelittlepony.unicopia.EquineContext;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.DirectionProperty;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.BlockMirror;
|
||||
import net.minecraft.util.BlockRotation;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
public class OrientedCloudBlock extends CloudBlock {
|
||||
public static final DirectionProperty FACING = Properties.FACING;
|
||||
|
||||
public OrientedCloudBlock(Settings settings, boolean meltable) {
|
||||
super(settings, meltable);
|
||||
this.setDefaultState(getDefaultState().with(FACING, Direction.UP));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
builder.add(FACING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState rotate(BlockState state, BlockRotation rotation) {
|
||||
return state.with(FACING, rotation.rotate(state.get(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState mirror(BlockState state, BlockMirror mirror) {
|
||||
return state.rotate(mirror.getRotation(state.get(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx, EquineContext equineContext) {
|
||||
return getDefaultState().with(FACING, ctx.getSide().getOpposite());
|
||||
}
|
||||
}
|
|
@ -20,21 +20,23 @@ public class PoreousCloudBlock extends CloudBlock implements Soakable {
|
|||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getSoggyState(int moisture) {
|
||||
return soggyBlock == null ? null : soggyBlock.get().getSoggyState(moisture);
|
||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||
if (moisture <= 0) {
|
||||
return Soakable.copyProperties(state, getDefaultState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMoisture(BlockState state) {
|
||||
return 0;
|
||||
return soggyBlock == null ? null : soggyBlock.get().getStateWithMoisture(state, moisture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
if (soggyBlock != null && world.hasRain(pos) && world.isAir(pos.up())) {
|
||||
world.setBlockState(pos, Soakable.copyProperties(state, soggyBlock.get().getSoggyState(random.nextBetween(1, 5))));
|
||||
if (state.getBlock() instanceof Soakable soakable && world.hasRain(pos) && world.isAir(pos.up())) {
|
||||
@Nullable
|
||||
BlockState soggyState = soakable.getStateWithMoisture(state, random.nextBetween(1, 5));
|
||||
if (soggyState != null) {
|
||||
world.setBlockState(pos, soggyState);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
super.randomTick(state, world, pos, random);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package com.minelittlepony.unicopia.block.cloud;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
|
||||
public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakable {
|
||||
|
||||
protected final Supplier<Soakable> soggyBlock;
|
||||
|
||||
public PoreousCloudStairsBlock(BlockState baseState, Settings settings, Supplier<Soakable> soggyBlock) {
|
||||
super(baseState, settings);
|
||||
this.soggyBlock = soggyBlock;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||
if (moisture <= 0) {
|
||||
return Soakable.copyProperties(state, getDefaultState());
|
||||
}
|
||||
return soggyBlock.get().getStateWithMoisture(state, moisture);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.minelittlepony.unicopia.block.cloud;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.EquineContext;
|
||||
import com.minelittlepony.unicopia.container.ShapingBenchScreenHandler;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory;
|
||||
import net.minecraft.screen.ScreenHandlerContext;
|
||||
import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
|
||||
import net.minecraft.stat.Stats;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ShapingBenchBlock extends CloudBlock {
|
||||
private static final VoxelShape SHAPE = VoxelShapes.union(
|
||||
Block.createCuboidShape(0, 13, 0, 3, 18, 3),
|
||||
Block.createCuboidShape(13, 13, 0, 16, 18, 3),
|
||||
Block.createCuboidShape(0, 13, 13, 3, 18, 16),
|
||||
Block.createCuboidShape(13, 13, 13, 16, 18, 16),
|
||||
Block.createCuboidShape(0, 13, 0, 16, 16, 16),
|
||||
Block.createCuboidShape(2, 0, 2, 14, 17, 14),
|
||||
Block.createCuboidShape(0, 0, 0, 16, 4, 16)
|
||||
);
|
||||
|
||||
public ShapingBenchBlock(Settings settings) {
|
||||
super(settings, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context, EquineContext equineContext) {
|
||||
return SHAPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
if (world.isClient) {
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
player.openHandledScreen(state.createScreenHandlerFactory(world, pos));
|
||||
player.incrementStat(Stats.INTERACT_WITH_STONECUTTER);
|
||||
return ActionResult.CONSUME;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) {
|
||||
return new SimpleNamedScreenHandlerFactory((syncId, playerInventory, player) -> {
|
||||
return new ShapingBenchScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos));
|
||||
}, getName());
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
@ -31,9 +30,12 @@ public interface Soakable {
|
|||
IntProperty MOISTURE = IntProperty.of("moisture", 1, 7);
|
||||
Direction[] DIRECTIONS = Arrays.stream(Direction.values()).filter(d -> d != Direction.UP).toArray(Direction[]::new);
|
||||
|
||||
BlockState getSoggyState(int moisture);
|
||||
@Nullable
|
||||
BlockState getStateWithMoisture(BlockState state, int moisture);
|
||||
|
||||
int getMoisture(BlockState state);
|
||||
default int getMoisture(BlockState state) {
|
||||
return state.getOrEmpty(MOISTURE).orElse(0);
|
||||
}
|
||||
|
||||
static void addMoistureParticles(BlockState state, World world, BlockPos pos, Random random) {
|
||||
if (random.nextInt(5) == 0) {
|
||||
|
@ -46,7 +48,8 @@ public interface Soakable {
|
|||
}
|
||||
}
|
||||
|
||||
static ActionResult tryCollectMoisture(Block dryBlock, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
static ActionResult tryCollectMoisture(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
if (state.getBlock() instanceof Soakable soakable) {
|
||||
ItemStack stack = player.getStackInHand(hand);
|
||||
if (stack.getItem() == Items.GLASS_BOTTLE) {
|
||||
if (!player.isCreative()) {
|
||||
|
@ -59,57 +62,58 @@ public interface Soakable {
|
|||
}
|
||||
world.playSound(player, player.getX(), player.getY(), player.getZ(), USounds.Vanilla.ITEM_BOTTLE_FILL, SoundCategory.NEUTRAL, 1, 1);
|
||||
world.emitGameEvent(player, GameEvent.FLUID_PICKUP, pos);
|
||||
updateMoisture(dryBlock, state, world, pos, state.get(MOISTURE) - 1);
|
||||
updateMoisture(soakable, state, world, pos, soakable.getMoisture(state) - 1);
|
||||
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
|
||||
static void tickMoisture(Block dryBlock, BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
int moisture = state.get(MOISTURE);
|
||||
static void tickMoisture(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
if (state.getBlock() instanceof Soakable soakable) {
|
||||
int moisture = soakable.getMoisture(state);
|
||||
|
||||
if (world.hasRain(pos) && world.isAir(pos.up())) {
|
||||
if (moisture < 7) {
|
||||
world.setBlockState(pos, state.with(MOISTURE, moisture + 1));
|
||||
world.setBlockState(pos, soakable.getStateWithMoisture(state, moisture + 1));
|
||||
}
|
||||
} else {
|
||||
if (moisture > 1) {
|
||||
BlockPos neighborPos = pos.offset(Util.getRandom(Soakable.DIRECTIONS, random));
|
||||
BlockState neighborState = world.getBlockState(neighborPos);
|
||||
|
||||
if (neighborState.getBlock() instanceof Soakable soakable && soakable.getMoisture(neighborState) < moisture) {
|
||||
if (neighborState.getBlock() instanceof Soakable neighborSoakable && neighborSoakable.getMoisture(neighborState) < moisture) {
|
||||
int half = Math.max(1, moisture / 2);
|
||||
@Nullable
|
||||
BlockState newNeighborState = soakable.getSoggyState(half);
|
||||
BlockState newNeighborState = neighborSoakable.getStateWithMoisture(neighborState, half);
|
||||
if (newNeighborState != null) {
|
||||
updateMoisture(dryBlock, state, world, pos, moisture - half);
|
||||
world.setBlockState(neighborPos, soakable.getSoggyState(half));
|
||||
updateMoisture(soakable, state, world, pos, moisture - half);
|
||||
world.setBlockState(neighborPos, soakable.getStateWithMoisture(state, half));
|
||||
world.emitGameEvent(null, GameEvent.BLOCK_CHANGE, neighborPos);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateMoisture(dryBlock, state, world, pos, moisture - 1);
|
||||
updateMoisture(soakable, state, world, pos, moisture - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateMoisture(Block dryBlock, BlockState state, World world, BlockPos pos, int newMoisture) {
|
||||
if (newMoisture <= 0) {
|
||||
world.setBlockState(pos, copyProperties(state, dryBlock.getDefaultState()));
|
||||
} else {
|
||||
world.setBlockState(pos, state.with(MOISTURE, newMoisture));
|
||||
}
|
||||
private static void updateMoisture(Soakable soakable, BlockState state, World world, BlockPos pos, int newMoisture) {
|
||||
world.setBlockState(pos, soakable.getStateWithMoisture(state, newMoisture));
|
||||
world.playSound(null, pos, SoundEvents.ENTITY_SALMON_FLOP, SoundCategory.BLOCKS, 1, (float)world.random.nextTriangular(0.5, 0.3F));
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
static BlockState copyProperties(BlockState from, BlockState to) {
|
||||
static BlockState copyProperties(BlockState from, @Nullable BlockState to) {
|
||||
if (to != null) {
|
||||
for (Property property : from.getProperties()) {
|
||||
to = to.withIfExists(property, from.get(property));
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@ package com.minelittlepony.unicopia.block.cloud;
|
|||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
|
@ -12,6 +15,7 @@ import net.minecraft.util.Hand;
|
|||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class SoggyCloudBlock extends CloudBlock implements Soakable {
|
||||
|
@ -24,25 +28,30 @@ public class SoggyCloudBlock extends CloudBlock implements Soakable {
|
|||
this.dryBlock = dryBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getSoggyState(int moisture) {
|
||||
return getDefaultState().with(MOISTURE, moisture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMoisture(BlockState state) {
|
||||
return state.get(MOISTURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder);
|
||||
builder.add(MOISTURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getPickStack(BlockView world, BlockPos pos, BlockState state) {
|
||||
return dryBlock.get().getPickStack(world, pos, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||
if (moisture <= 0) {
|
||||
return Soakable.copyProperties(state, dryBlock.get().getDefaultState());
|
||||
}
|
||||
return Soakable.copyProperties(state, getDefaultState()).with(MOISTURE, moisture);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
return Soakable.tryCollectMoisture(dryBlock.get(), state, world, pos, player, hand, hit);
|
||||
return Soakable.tryCollectMoisture(state, world, pos, player, hand, hit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,6 +62,6 @@ public class SoggyCloudBlock extends CloudBlock implements Soakable {
|
|||
|
||||
@Override
|
||||
public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
Soakable.tickMoisture(dryBlock.get(), state, world, pos, random);
|
||||
Soakable.tickMoisture(state, world, pos, random);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@ package com.minelittlepony.unicopia.block.cloud;
|
|||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
|
@ -12,6 +15,7 @@ import net.minecraft.util.Hand;
|
|||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class SoggyCloudSlabBlock extends CloudSlabBlock {
|
||||
|
@ -24,26 +28,30 @@ public class SoggyCloudSlabBlock extends CloudSlabBlock {
|
|||
this.dryBlock = dryBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getSoggyState(int moisture) {
|
||||
return getDefaultState().with(MOISTURE, moisture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMoisture(BlockState state) {
|
||||
return state.get(MOISTURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder);
|
||||
builder.add(MOISTURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getPickStack(BlockView world, BlockPos pos, BlockState state) {
|
||||
return dryBlock.get().getPickStack(world, pos, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||
if (moisture <= 0) {
|
||||
return Soakable.copyProperties(state, dryBlock.get().getDefaultState());
|
||||
}
|
||||
return Soakable.copyProperties(state, getDefaultState()).with(MOISTURE, moisture);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
return Soakable.tryCollectMoisture(dryBlock.get(), state, world, pos, player, hand, hit);
|
||||
return Soakable.tryCollectMoisture(state, world, pos, player, hand, hit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,6 +62,6 @@ public class SoggyCloudSlabBlock extends CloudSlabBlock {
|
|||
|
||||
@Override
|
||||
public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||
Soakable.tickMoisture(dryBlock.get(), state, world, pos, random);
|
||||
Soakable.tickMoisture(state, world, pos, random);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package com.minelittlepony.unicopia.block.cloud;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockView;
|
||||
|
||||
public class SoggyCloudStairsBlock extends CloudStairsBlock implements Soakable {
|
||||
|
||||
private final Supplier<Block> dryBlock;
|
||||
|
||||
public SoggyCloudStairsBlock(BlockState baseState, Settings settings, Supplier<Block> dryBlock) {
|
||||
super(baseState, settings);
|
||||
setDefaultState(getDefaultState().with(MOISTURE, 7));
|
||||
this.dryBlock = dryBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder);
|
||||
builder.add(MOISTURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getPickStack(BlockView world, BlockPos pos, BlockState state) {
|
||||
return dryBlock.get().getPickStack(world, pos, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||
if (moisture <= 0) {
|
||||
return Soakable.copyProperties(state, dryBlock.get().getDefaultState());
|
||||
}
|
||||
return Soakable.copyProperties(state, getDefaultState()).with(MOISTURE, moisture);
|
||||
}
|
||||
}
|
|
@ -59,6 +59,10 @@ public class UnstableCloudBlock extends CloudBlock {
|
|||
}
|
||||
world.playSound(null, pos, SoundEvents.BLOCK_WOOL_HIT, SoundCategory.BLOCKS, 1, 1);
|
||||
|
||||
if (world.isClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fallDistance > 3) {
|
||||
world.breakBlock(pos, true);
|
||||
return;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package com.minelittlepony.unicopia.client;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.block.*;
|
||||
import com.minelittlepony.unicopia.block.cloud.CloudChestBlock;
|
||||
import com.minelittlepony.unicopia.client.particle.ChangelingMagicParticle;
|
||||
import com.minelittlepony.unicopia.client.particle.CloudsEscapingParticle;
|
||||
import com.minelittlepony.unicopia.client.particle.DiskParticle;
|
||||
|
@ -21,8 +26,8 @@ import com.minelittlepony.unicopia.client.render.spell.SpellRendererFactory;
|
|||
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||
import com.minelittlepony.unicopia.item.ChameleonItem;
|
||||
import com.minelittlepony.unicopia.item.EnchantableItem;
|
||||
import com.minelittlepony.unicopia.item.FancyBedItem;
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
import com.minelittlepony.unicopia.item.cloud.CloudBedItem;
|
||||
import com.minelittlepony.unicopia.particle.UParticles;
|
||||
import com.terraformersmc.terraform.boat.api.client.TerraformBoatClientHelper;
|
||||
|
||||
|
@ -30,9 +35,11 @@ import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
|||
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry;
|
||||
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry.PendingParticleFactory;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.*;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry.DynamicItemRenderer;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.color.block.BlockColorProvider;
|
||||
import net.minecraft.client.color.world.BiomeColors;
|
||||
import net.minecraft.client.color.world.FoliageColors;
|
||||
import net.minecraft.client.item.ModelPredicateProviderRegistry;
|
||||
|
@ -43,13 +50,18 @@ import net.minecraft.client.render.block.entity.BlockEntityRendererFactories;
|
|||
import net.minecraft.client.render.entity.FlyingItemEntityRenderer;
|
||||
import net.minecraft.client.render.item.ItemRenderer;
|
||||
import net.minecraft.client.render.model.json.ModelTransformationMode;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.particle.ParticleEffect;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
public interface URenderers {
|
||||
BlockEntity CHEST_RENDER_ENTITY = new CloudChestBlock.TileData(BlockPos.ORIGIN, UBlocks.CLOUD_CHEST.getDefaultState());
|
||||
|
||||
static void bootstrap() {
|
||||
ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new));
|
||||
ParticleFactoryRegistry.getInstance().register(UParticles.CHANGELING_MAGIC, createFactory(ChangelingMagicParticle::new));
|
||||
|
@ -65,14 +77,11 @@ public interface URenderers {
|
|||
ParticleFactoryRegistry.getInstance().register(UParticles.CLOUDS_ESCAPING, CloudsEscapingParticle::new);
|
||||
ParticleFactoryRegistry.getInstance().register(UParticles.LIGHTNING_BOLT, LightningBoltParticle::new);
|
||||
|
||||
AccessoryFeatureRenderer.register(BraceletFeatureRenderer::new);
|
||||
AccessoryFeatureRenderer.register(AmuletFeatureRenderer::new);
|
||||
AccessoryFeatureRenderer.register(WingsFeatureRenderer::new);
|
||||
AccessoryFeatureRenderer.register(HornFeatureRenderer::new);
|
||||
AccessoryFeatureRenderer.register(IcarusWingsFeatureRenderer::new);
|
||||
AccessoryFeatureRenderer.register(BatWingsFeatureRenderer::new);
|
||||
AccessoryFeatureRenderer.register(GlassesFeatureRenderer::new);
|
||||
AccessoryFeatureRenderer.register(HeldEntityFeatureRenderer::new);
|
||||
AccessoryFeatureRenderer.register(
|
||||
BraceletFeatureRenderer::new, AmuletFeatureRenderer::new, GlassesFeatureRenderer::new,
|
||||
WingsFeatureRenderer::new, HornFeatureRenderer::new, IcarusWingsFeatureRenderer::new, BatWingsFeatureRenderer::new,
|
||||
HeldEntityFeatureRenderer::new
|
||||
);
|
||||
|
||||
EntityRendererRegistry.register(UEntities.THROWN_ITEM, FlyingItemEntityRenderer::new);
|
||||
EntityRendererRegistry.register(UEntities.MUFFIN, FlyingItemEntityRenderer::new);
|
||||
|
@ -87,13 +96,53 @@ public interface URenderers {
|
|||
EntityRendererRegistry.register(UEntities.STORM_CLOUD, StormCloudEntityRenderer::new);
|
||||
EntityRendererRegistry.register(UEntities.AIR_BALLOON, AirBalloonEntityRenderer::new);
|
||||
EntityRendererRegistry.register(UEntities.FRIENDLY_CREEPER, FriendlyCreeperEntityRenderer::new);
|
||||
EntityRendererRegistry.register(UEntities.LOOT_BUG, LootBugEntityRenderer::new);
|
||||
|
||||
BlockEntityRendererFactories.register(UBlockEntities.WEATHER_VANE, WeatherVaneBlockEntityRenderer::new);
|
||||
BlockEntityRendererFactories.register(UBlockEntities.CLOUD_BED, CloudBedBlockEntityRenderer::new);
|
||||
BlockEntityRendererFactories.register(UBlockEntities.FANCY_BED, CloudBedBlockEntityRenderer::new);
|
||||
BlockEntityRendererFactories.register(UBlockEntities.CLOUD_CHEST, CloudChestBlockEntityRenderer::new);
|
||||
|
||||
register(URenderers::renderJarItem, UItems.FILLED_JAR);
|
||||
register(URenderers::renderBedItem, UItems.CLOTH_BED, UItems.CLOUD_BED);
|
||||
register(URenderers::renderChestItem, UBlocks.CLOUD_CHEST.asItem());
|
||||
PolearmRenderer.register(UItems.WOODEN_POLEARM, UItems.STONE_POLEARM, UItems.IRON_POLEARM, UItems.GOLDEN_POLEARM, UItems.DIAMOND_POLEARM, UItems.NETHERITE_POLEARM);
|
||||
ModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("affinity"), (stack, world, entity, seed) -> EnchantableItem.isEnchanted(stack) ? EnchantableItem.getSpellKey(stack).getAffinity().getAlignment() : 0);
|
||||
ModelPredicateProviderRegistry.register(UItems.ROCK_CANDY, new Identifier("count"), (stack, world, entity, seed) -> stack.getCount() / (float)stack.getMaxCount());
|
||||
|
||||
ColorProviderRegistry.BLOCK.register(URenderers::getTintedBlockColor, TintedBlock.REGISTRY.stream().toArray(Block[]::new));
|
||||
ColorProviderRegistry.ITEM.register((stack, i) -> getTintedBlockColor(Block.getBlockFromItem(stack.getItem()).getDefaultState(), null, null, i), TintedBlock.REGISTRY.stream().map(Block::asItem).filter(i -> i != Items.AIR).toArray(Item[]::new));
|
||||
ColorProviderRegistry.ITEM.register((stack, i) -> i > 0 ? -1 : ((DyeableItem)stack.getItem()).getColor(stack), UItems.FRIENDSHIP_BRACELET);
|
||||
BuiltinItemRendererRegistry.INSTANCE.register(UItems.FILLED_JAR, (stack, mode, matrices, vertices, light, overlay) -> {
|
||||
ColorProviderRegistry.ITEM.register((stack, i) -> i > 0 || !EnchantableItem.isEnchanted(stack) ? -1 : EnchantableItem.getSpellKey(stack).getColor(), UItems.GEMSTONE);
|
||||
ColorProviderRegistry.ITEM.register((stack, i) -> i == 1 && EnchantableItem.isEnchanted(stack) ? EnchantableItem.getSpellKey(stack).getColor() : -1, UItems.MAGIC_STAFF);
|
||||
|
||||
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), UBlocks.TRANSLUCENT_BLOCKS.stream().toArray(Block[]::new));
|
||||
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getTranslucent(), UBlocks.SEMI_TRANSPARENT_BLOCKS.stream().toArray(Block[]::new));
|
||||
// for lava boats
|
||||
BlockRenderLayerMap.INSTANCE.putFluids(RenderLayer.getTranslucent(), Fluids.LAVA, Fluids.FLOWING_LAVA);
|
||||
|
||||
TerraformBoatClientHelper.registerModelLayers(Unicopia.id("palm"), false);
|
||||
|
||||
SpellRendererFactory.bootstrap();
|
||||
}
|
||||
|
||||
private static void register(DynamicItemRenderer renderer, ItemConvertible...items) {
|
||||
for (ItemConvertible item : items) {
|
||||
BuiltinItemRendererRegistry.INSTANCE.register(item, renderer);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void renderBedItem(ItemStack stack, ModelTransformationMode mode, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
|
||||
BlockEntity entity = ((Supplier<BlockEntity>)stack.getItem()).get();
|
||||
((FancyBedBlock.Tile)entity).setPattern(FancyBedItem.getPattern(stack));
|
||||
MinecraftClient.getInstance().getBlockEntityRenderDispatcher().renderEntity(entity, matrices, vertices, light, overlay);
|
||||
}
|
||||
|
||||
private static void renderChestItem(ItemStack stack, ModelTransformationMode mode, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
|
||||
MinecraftClient.getInstance().getBlockEntityRenderDispatcher().renderEntity(CHEST_RENDER_ENTITY, matrices, vertices, light, overlay);
|
||||
}
|
||||
|
||||
private static void renderJarItem(ItemStack stack, ModelTransformationMode mode, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
|
||||
ItemRenderer renderer = MinecraftClient.getInstance().getItemRenderer();
|
||||
|
||||
ChameleonItem item = (ChameleonItem)stack.getItem();
|
||||
|
@ -134,34 +183,9 @@ public interface URenderers {
|
|||
DiffuseLighting.enableGuiDepthLighting();
|
||||
}
|
||||
matrices.push();
|
||||
|
||||
});
|
||||
BuiltinItemRendererRegistry.INSTANCE.register(UItems.CLOUD_BED, (stack, mode, matrices, vertices, light, overlay) -> {
|
||||
MinecraftClient.getInstance().getBlockEntityRenderDispatcher().renderEntity(((CloudBedItem)stack.getItem()).getRenderEntity(), matrices, vertices, light, overlay);
|
||||
});
|
||||
PolearmRenderer.register(UItems.WOODEN_POLEARM);
|
||||
PolearmRenderer.register(UItems.STONE_POLEARM);
|
||||
PolearmRenderer.register(UItems.IRON_POLEARM);
|
||||
PolearmRenderer.register(UItems.GOLDEN_POLEARM);
|
||||
PolearmRenderer.register(UItems.DIAMOND_POLEARM);
|
||||
PolearmRenderer.register(UItems.NETHERITE_POLEARM);
|
||||
ModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("affinity"), (stack, world, entity, seed) -> {
|
||||
return EnchantableItem.isEnchanted(stack) ? EnchantableItem.getSpellKey(stack).getAffinity().getAlignment() : 0;
|
||||
});
|
||||
ModelPredicateProviderRegistry.register(UItems.ROCK_CANDY, new Identifier("count"), (stack, world, entity, seed) -> {
|
||||
return stack.getCount() / (float)stack.getMaxCount();
|
||||
});
|
||||
ColorProviderRegistry.ITEM.register((stack, i) -> {
|
||||
return i > 0 || !EnchantableItem.isEnchanted(stack) ? -1 : EnchantableItem.getSpellKey(stack).getColor();
|
||||
}, UItems.GEMSTONE);
|
||||
ColorProviderRegistry.ITEM.register((stack, i) -> {
|
||||
if (i == 1 && EnchantableItem.isEnchanted(stack)) {
|
||||
return EnchantableItem.getSpellKey(stack).getColor();
|
||||
}
|
||||
return -1;
|
||||
}, UItems.MAGIC_STAFF);
|
||||
|
||||
BlockColorProvider tintedProvider = (state, view, pos, color) -> {
|
||||
private static int getTintedBlockColor(BlockState state, @Nullable BlockRenderView view, @Nullable BlockPos pos, int color) {
|
||||
if (view == null || pos == null) {
|
||||
color = FoliageColors.getDefaultColor();
|
||||
} else {
|
||||
|
@ -173,21 +197,6 @@ public interface URenderers {
|
|||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
ColorProviderRegistry.BLOCK.register(tintedProvider, TintedBlock.REGISTRY.stream().toArray(Block[]::new));
|
||||
ColorProviderRegistry.ITEM.register((stack, i) -> {
|
||||
return tintedProvider.getColor(Block.getBlockFromItem(stack.getItem()).getDefaultState(), null, null, i);
|
||||
}, TintedBlock.REGISTRY.stream().map(Block::asItem).filter(i -> i != Items.AIR).toArray(Item[]::new));
|
||||
|
||||
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), UBlocks.TRANSLUCENT_BLOCKS.stream().toArray(Block[]::new));
|
||||
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getTranslucent(), UBlocks.SEMI_TRANSPARENT_BLOCKS.stream().toArray(Block[]::new));
|
||||
// for lava boats
|
||||
BlockRenderLayerMap.INSTANCE.putFluids(RenderLayer.getTranslucent(), Fluids.LAVA, Fluids.FLOWING_LAVA);
|
||||
|
||||
TerraformBoatClientHelper.registerModelLayers(Unicopia.id("palm"), false);
|
||||
|
||||
SpellRendererFactory.bootstrap();
|
||||
}
|
||||
|
||||
static <T extends ParticleEffect> PendingParticleFactory<T> createFactory(ParticleSupplier<T> supplier) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.InteractionManager;
|
|||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.client.gui.LanSettingsScreen;
|
||||
import com.minelittlepony.unicopia.client.gui.ShapingBenchScreen;
|
||||
import com.minelittlepony.unicopia.client.gui.UHud;
|
||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
|
||||
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
|
||||
|
@ -101,6 +102,7 @@ public class UnicopiaClient implements ClientModInitializer {
|
|||
URenderers.bootstrap();
|
||||
|
||||
HandledScreens.register(UScreenHandlers.SPELL_BOOK, SpellbookScreen::new);
|
||||
HandledScreens.register(UScreenHandlers.SHAPING_BENCH, ShapingBenchScreen::new);
|
||||
|
||||
ClientTickEvents.END_CLIENT_TICK.register(this::onTick);
|
||||
ClientTickEvents.END_WORLD_TICK.register(this::onWorldTick);
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package com.minelittlepony.unicopia.client.gui;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.entity.duck.EntityDuck;
|
||||
import com.minelittlepony.unicopia.entity.effect.EffectUtils;
|
||||
import com.minelittlepony.unicopia.entity.effect.UEffects;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
|
||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
||||
import net.minecraft.entity.effect.StatusEffects;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.registry.tag.FluidTags;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
|
||||
public class HudEffects {
|
||||
|
||||
private static boolean addedHunger;
|
||||
private static Set<TagKey<Fluid>> originalTags = null;
|
||||
|
||||
public static void tryApply(@Nullable PlayerEntity player, float tickDelta, boolean on) {
|
||||
if (player != null) {
|
||||
apply(Pony.of(player), tickDelta, on);
|
||||
}
|
||||
}
|
||||
|
||||
private static void apply(Pony pony, float tickDelta, boolean on) {
|
||||
if (on) {
|
||||
if (!pony.asEntity().hasStatusEffect(StatusEffects.HUNGER) && EffectUtils.getAmplifier(pony.asEntity(), UEffects.FOOD_POISONING) > 0) {
|
||||
addedHunger = true;
|
||||
pony.asEntity().addStatusEffect(new StatusEffectInstance(StatusEffects.HUNGER, 1, 1, false, false));
|
||||
}
|
||||
} else {
|
||||
if (addedHunger) {
|
||||
addedHunger = false;
|
||||
pony.asEntity().removeStatusEffect(StatusEffects.HUNGER);
|
||||
}
|
||||
}
|
||||
|
||||
if (pony.getCompositeRace().includes(Race.SEAPONY)) {
|
||||
Set<TagKey<Fluid>> fluidTags = ((EntityDuck)pony.asEntity()).getSubmergedFluidTags();
|
||||
if (on) {
|
||||
originalTags = new HashSet<>(fluidTags);
|
||||
if (fluidTags.contains(FluidTags.WATER)) {
|
||||
fluidTags.clear();
|
||||
} else {
|
||||
fluidTags.add(FluidTags.WATER);
|
||||
}
|
||||
} else if (originalTags != null) {
|
||||
fluidTags.clear();
|
||||
fluidTags.addAll(originalTags);
|
||||
originalTags = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -113,7 +113,7 @@ public class LanSettingsScreen extends GameGui {
|
|||
WHITELIST_GRID_PACKER.start();
|
||||
|
||||
for (Race race : Race.REGISTRY) {
|
||||
if (!race.isUnset()) {
|
||||
if (!race.isUnset() && race.availability().isGrantable()) {
|
||||
Bounds bound = WHITELIST_GRID_PACKER.next();
|
||||
|
||||
Button button = content.addButton(new Toggle(LEFT + bound.left + 10, row + bound.top, whitelist.contains(race.getId().toString())))
|
||||
|
|
|
@ -13,7 +13,7 @@ import net.minecraft.client.util.math.MatrixStack;
|
|||
class ManaRingSlot extends Slot {
|
||||
|
||||
public ManaRingSlot(UHud uHud, AbilitySlot normalSlot, AbilitySlot backupSlot, int x, int y) {
|
||||
super(uHud, normalSlot, backupSlot, x, y, 8, UHud.PRIMARY_SLOT_SIZE, 33, 43, 42);
|
||||
super(uHud, normalSlot, backupSlot, x, y, 8, UHud.PRIMARY_SLOT_SIZE, 33, 43, 30);
|
||||
background(0, 5);
|
||||
foreground(0, 59);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package com.minelittlepony.unicopia.client.gui;
|
||||
|
||||
import net.minecraft.client.gui.screen.ingame.StonecutterScreen;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.screen.StonecutterScreenHandler;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class ShapingBenchScreen extends StonecutterScreen {
|
||||
public ShapingBenchScreen(StonecutterScreenHandler handler, PlayerInventory inventory, Text title) {
|
||||
super(handler, inventory, title);
|
||||
}
|
||||
}
|
|
@ -93,8 +93,9 @@ class Slot {
|
|||
|
||||
AbilityDispatcher.Stat stat = abilities.getStat(bSwap ? bSlot : aSlot);
|
||||
|
||||
int iconPosition = ((size - iconSize + slotPadding + 1) / 2);
|
||||
int sz = iconSize - slotPadding;
|
||||
uHud.renderAbilityIcon(context, stat, slotPadding, slotPadding, sz, sz, sz, sz);
|
||||
uHud.renderAbilityIcon(context, stat, iconPosition, iconPosition, sz, sz, sz, sz);
|
||||
|
||||
float cooldown = stat.getFillProgress();
|
||||
|
||||
|
|
|
@ -13,13 +13,13 @@ public class TextBlock extends Label {
|
|||
public TextBlock(int x, int y, int width) {
|
||||
super(x, y);
|
||||
this.maxWidth = width;
|
||||
this.render(null, x, y, width);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bounds getBounds() {
|
||||
Bounds bounds = super.getBounds();
|
||||
bounds.height = getFont().wrapLines(getStyle().getText(), maxWidth).size() * getFont().fontHeight;
|
||||
bounds.width = 0;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,10 +41,10 @@ public class TribeButton extends Button {
|
|||
MinecraftClient mc = MinecraftClient.getInstance();
|
||||
|
||||
context.drawTexture(TribeSelectionScreen.TEXTURE, getX() - 3, getY() - 13, 0, 0, 76, 69);
|
||||
if (isHovered()) {
|
||||
if (isSelected()) {
|
||||
context.drawTexture(TribeSelectionScreen.TEXTURE, getX() - 4, getY() - 14, 76, 0, 78, 71);
|
||||
|
||||
if (hovered && screenWidth > 0) {
|
||||
if (isFocused() && screenWidth > 0) {
|
||||
Identifier id = Race.REGISTRY.getId(race);
|
||||
context.drawCenteredTextWithShadow(getFont(), Text.translatable("gui.unicopia.tribe_selection.describe." + id.getNamespace() + "." + id.getPath()), screenWidth / 2, getY() + height, 0xFFFFFFFF);
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ public class TribeButton extends Button {
|
|||
int foreColor = getStyle().getColor();
|
||||
if (!active) {
|
||||
foreColor = 10526880;
|
||||
} else if (isHovered()) {
|
||||
} else if (isSelected()) {
|
||||
foreColor = 16777120;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.client.gui;
|
|||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.minelittlepony.common.client.gui.GameGui;
|
||||
import com.minelittlepony.common.client.gui.ScrollContainer;
|
||||
import com.minelittlepony.common.client.gui.element.Button;
|
||||
import com.minelittlepony.common.client.gui.element.Label;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
|
@ -17,6 +18,8 @@ public class TribeConfirmationScreen extends GameGui implements HidesHud {
|
|||
|
||||
private final BooleanConsumer callback;
|
||||
|
||||
private final ScrollContainer textBody = new ScrollContainer();
|
||||
|
||||
public TribeConfirmationScreen(BooleanConsumer callback, Race selection) {
|
||||
super(Text.translatable("gui.unicopia.tribe_selection"));
|
||||
this.callback = callback;
|
||||
|
@ -25,12 +28,26 @@ public class TribeConfirmationScreen extends GameGui implements HidesHud {
|
|||
|
||||
@Override
|
||||
protected void init() {
|
||||
|
||||
final int columnHeight = 167;
|
||||
final int columnWidth = 310;
|
||||
final int padding = 15;
|
||||
|
||||
int top = (height - columnHeight) / 2;
|
||||
int left = (width - columnWidth) / 2 + 8;
|
||||
int maxWidth = 295;
|
||||
|
||||
textBody.verticalScrollbar.layoutToEnd = true;
|
||||
textBody.margin.top = top + 43;
|
||||
textBody.margin.left = left;
|
||||
textBody.margin.right = width - (left + maxWidth);
|
||||
textBody.margin.bottom = height - (textBody.margin.top + 130);
|
||||
textBody.getContentPadding().top = 10;
|
||||
textBody.getContentPadding().left = 8;
|
||||
textBody.getContentPadding().bottom = 100;
|
||||
textBody.getContentPadding().right = 0;
|
||||
textBody.init(this::buildTextBody);
|
||||
|
||||
getChildElements().add(textBody);
|
||||
|
||||
addDrawableChild(new Button(width / 2 + 5, top + columnHeight + padding, 100, 20))
|
||||
.onClick(b -> callback.accept(true))
|
||||
|
@ -42,38 +59,40 @@ public class TribeConfirmationScreen extends GameGui implements HidesHud {
|
|||
addDrawable(new Label(width / 2, top - 30).setCentered()).getStyle().setText(Text.translatable("gui.unicopia.tribe_selection.confirm", selection.getDisplayName().copy().formatted(Formatting.YELLOW)));
|
||||
|
||||
addDrawable(new TribeButton((width - 70) / 2, top, 0, selection));
|
||||
}
|
||||
|
||||
top += 43;
|
||||
private void buildTextBody() {
|
||||
int top = 0;
|
||||
|
||||
int left = (width - columnWidth) / 2 + padding;
|
||||
int left = 0;
|
||||
|
||||
Text race = selection.getAltDisplayName().copy().formatted(Formatting.YELLOW);
|
||||
|
||||
addDrawable(new Label(left - 3, top += 10)).getStyle().setText(Text.translatable("gui.unicopia.tribe_selection.confirm.goods", race).formatted(Formatting.YELLOW));
|
||||
textBody.addButton(new Label(left - 3, top += 10)).getStyle().setText(Text.translatable("gui.unicopia.tribe_selection.confirm.goods", race).formatted(Formatting.YELLOW));
|
||||
|
||||
top += 15;
|
||||
|
||||
int maxWidth = 280;
|
||||
int maxWidth = 270;
|
||||
|
||||
Identifier id = Race.REGISTRY.getId(selection);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
String key = String.format("gui.unicopia.tribe_selection.confirm.goods.%d.%s.%s", i, id.getNamespace(), id.getPath());
|
||||
if (Language.getInstance().hasTranslation(key)) {
|
||||
TextBlock block = addDrawable(new TextBlock(left, top, maxWidth));
|
||||
TextBlock block = textBody.addButton(new TextBlock(left, top, maxWidth));
|
||||
block.getStyle().setText(Text.translatable(key));
|
||||
top += block.getBounds().height;
|
||||
}
|
||||
}
|
||||
|
||||
addDrawable(new Label(left - 3, top += 5)).getStyle().setText(Text.translatable("gui.unicopia.tribe_selection.confirm.bads", race).formatted(Formatting.YELLOW));
|
||||
textBody.addButton(new Label(left - 3, top += 5)).getStyle().setText(Text.translatable("gui.unicopia.tribe_selection.confirm.bads", race).formatted(Formatting.YELLOW));
|
||||
|
||||
top += 15;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
String key = String.format("gui.unicopia.tribe_selection.confirm.bads.%d.%s.%s", i, id.getNamespace(), id.getPath());
|
||||
if (Language.getInstance().hasTranslation(key)) {
|
||||
TextBlock block = addDrawable(new TextBlock(left, top, maxWidth));
|
||||
TextBlock block = textBody.addButton(new TextBlock(left, top, maxWidth));
|
||||
block.getStyle().setText(Text.translatable(key));
|
||||
top += block.getBounds().height;
|
||||
}
|
||||
|
@ -81,9 +100,7 @@ public class TribeConfirmationScreen extends GameGui implements HidesHud {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
super.renderBackground(context, mouseX, mouseY, delta);
|
||||
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
final int columnHeight = 180;
|
||||
final int columnWidth = 310;
|
||||
final int segmentWidth = 123;
|
||||
|
@ -91,7 +108,7 @@ public class TribeConfirmationScreen extends GameGui implements HidesHud {
|
|||
int top = (height - columnHeight) / 2;
|
||||
int left = (width - columnWidth) / 2;
|
||||
|
||||
top += 25;
|
||||
top += 40;
|
||||
|
||||
final int zOffset = 0;
|
||||
|
||||
|
@ -106,10 +123,16 @@ public class TribeConfirmationScreen extends GameGui implements HidesHud {
|
|||
left = width / 2;
|
||||
|
||||
context.drawTexture(TribeSelectionScreen.TEXTURE, left - 55, top, 140, 70, 21, 50);
|
||||
|
||||
context.drawTexture(TribeSelectionScreen.TEXTURE, left - 35, top, 10, 70, 69, 50);
|
||||
|
||||
context.drawTexture(TribeSelectionScreen.TEXTURE, left + 35, top, 148, 70, 21, 50);
|
||||
|
||||
textBody.render(context, mouseX, mouseY, delta);
|
||||
|
||||
context.getMatrices().push();
|
||||
context.getMatrices().translate(0, 0, 2);
|
||||
context.drawTexture(TribeSelectionScreen.TEXTURE, left - 35, top - 5, 10, 70, 69, 50);
|
||||
context.drawTexture(TribeSelectionScreen.TEXTURE, left - 35, top - 15, 10, 70, 69, 50);
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
context.getMatrices().pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -120,7 +143,11 @@ public class TribeConfirmationScreen extends GameGui implements HidesHud {
|
|||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
||||
|
||||
callback.accept(false);
|
||||
return true;
|
||||
}
|
||||
if (keyCode == GLFW.GLFW_KEY_ENTER) {
|
||||
callback.accept(true);
|
||||
return true;
|
||||
}
|
||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package com.minelittlepony.unicopia.client.gui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.minelittlepony.common.client.gui.GameGui;
|
||||
import com.minelittlepony.common.client.gui.element.Label;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
|
@ -10,9 +13,12 @@ import com.minelittlepony.unicopia.Unicopia;
|
|||
import com.minelittlepony.unicopia.network.Channel;
|
||||
import com.minelittlepony.unicopia.network.MsgRequestSpeciesChange;
|
||||
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class TribeSelectionScreen extends GameGui implements HidesHud {
|
||||
static final Identifier TEXTURE = Unicopia.id("textures/gui/tribe_selection.png");
|
||||
|
@ -24,6 +30,13 @@ public class TribeSelectionScreen extends GameGui implements HidesHud {
|
|||
|
||||
private boolean finished;
|
||||
|
||||
private final List<TribeButton> options = new ArrayList<>();
|
||||
private static int SELECTION = -1;
|
||||
|
||||
private int prevScrollPosition;
|
||||
private int scrollPosition;
|
||||
private int targetScroll;
|
||||
|
||||
public TribeSelectionScreen(Set<Race> allowedRaces, String baseString) {
|
||||
super(Text.translatable(baseString));
|
||||
this.allowedRaces = allowedRaces;
|
||||
|
@ -51,33 +64,26 @@ public class TribeSelectionScreen extends GameGui implements HidesHud {
|
|||
top += block.getBounds().height;
|
||||
top += 30;
|
||||
|
||||
final int itemWidth = 70 + 10;
|
||||
|
||||
List<Race> options = Race.REGISTRY.stream().filter(race -> !race.isHuman() && !race.isOp()).toList();
|
||||
|
||||
int columns = Math.min(width / itemWidth, options.size());
|
||||
|
||||
int x = (width - (columns * itemWidth)) / 2;
|
||||
int y = top;
|
||||
|
||||
int column = 0;
|
||||
int row = 0;
|
||||
List<Race> options = Race.REGISTRY.stream().filter(race -> race.availability().isSelectable()).toList();
|
||||
this.options.clear();
|
||||
|
||||
for (Race race : options) {
|
||||
addOption(race, x + (column * itemWidth), y + (row * itemWidth));
|
||||
column++;
|
||||
if (column >= columns) {
|
||||
column = 0;
|
||||
row++;
|
||||
}
|
||||
addOption(race, top);
|
||||
}
|
||||
|
||||
top = height - 20;
|
||||
if (SELECTION == -1) {
|
||||
SELECTION = options.size() / 2;
|
||||
}
|
||||
scroll(SELECTION, false);
|
||||
}
|
||||
|
||||
private void addOption(Race race, int x, int y) {
|
||||
addDrawableChild(new TribeButton(x, y, width, race)).onClick(b -> {
|
||||
private void addOption(Race race, int y) {
|
||||
var option = new TribeButton(0, y, width, race);
|
||||
int index = options.size();
|
||||
options.add(addDrawableChild(option));
|
||||
option.onClick(b -> {
|
||||
finished = true;
|
||||
scroll(index, false);
|
||||
client.setScreen(new TribeConfirmationScreen(result -> {
|
||||
finished = false;
|
||||
|
||||
|
@ -91,12 +97,88 @@ public class TribeSelectionScreen extends GameGui implements HidesHud {
|
|||
}).setEnabled(allowedRaces.contains(race));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
prevScrollPosition = scrollPosition;
|
||||
if (scrollPosition < targetScroll) {
|
||||
scrollPosition++;
|
||||
}
|
||||
if (scrollPosition > targetScroll) {
|
||||
scrollPosition--;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateScolling() {
|
||||
final int itemWidth = 70 + 10;
|
||||
int x = (width - itemWidth) / 2;
|
||||
float diff = MathHelper.lerp(client.getTickDelta(), prevScrollPosition, scrollPosition) / 4F;
|
||||
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
var option = options.get(i);
|
||||
option.setX((int)(x + (i - diff) * itemWidth));
|
||||
option.setFocused(i == SELECTION);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
updateScolling();
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
|
||||
if (!options.isEmpty()) {
|
||||
var element = options.get(0);
|
||||
|
||||
float diff = (targetScroll - MathHelper.lerp(client.getTickDelta(), prevScrollPosition, scrollPosition)) * 7;
|
||||
context.drawTexture(TEXTURE, (width / 2) + 40 + (scrollPosition < targetScroll ? (int)diff : 0), element.getY() - 20, 10, 165, 153, 30, 85, 312, 312);
|
||||
context.drawTexture(TEXTURE, (width / 2) - 80 + (scrollPosition > targetScroll ? (int)diff : 0), element.getY() - 20, 10, 195, 153, 30, 85, 312, 312);
|
||||
if (element.getBounds().left < 0) {
|
||||
context.drawTexture(TEXTURE, 20, element.getY() - 10, 10, 188, 235, 24, 60, 312, 312);
|
||||
}
|
||||
element = options.get(options.size() - 1);
|
||||
if (element.getBounds().right() > width) {
|
||||
context.drawTexture(TEXTURE, width - 50, element.getY() - 10, 10, 164, 235, 24, 60, 312, 312);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
finished = true;
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
|
||||
if (keyCode == GLFW.GLFW_KEY_LEFT) {
|
||||
scroll(Math.max(SELECTION - 1, 0), true);
|
||||
return true;
|
||||
}
|
||||
if (keyCode == GLFW.GLFW_KEY_RIGHT) {
|
||||
scroll(Math.min(SELECTION + 1, options.size() - 1), true);
|
||||
return true;
|
||||
}
|
||||
if (keyCode == GLFW.GLFW_KEY_ENTER) {
|
||||
options.get(SELECTION).onPress();
|
||||
}
|
||||
|
||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
private void scroll(int target, boolean animate) {
|
||||
if (target == SELECTION) {
|
||||
return;
|
||||
}
|
||||
SELECTION = target;
|
||||
targetScroll = SELECTION * 4;
|
||||
if (!animate) {
|
||||
scrollPosition = targetScroll;
|
||||
prevScrollPosition = scrollPosition;
|
||||
} else {
|
||||
playSound(SoundEvents.UI_BUTTON_CLICK);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCloseOnEsc() {
|
||||
return false;
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.minelittlepony.client.model.entity.race.UnicornModel;
|
|||
import com.minelittlepony.client.model.part.UnicornHorn;
|
||||
import com.minelittlepony.mson.api.MsonModel;
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.FlightType;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.entity.AmuletSelectors;
|
||||
|
@ -43,7 +44,7 @@ class BodyPartGear<M extends ClientPonyModel<LivingEntity>> implements Gear {
|
|||
public static final Predicate<LivingEntity> UNICORN_HORN_PREDICATE = MINE_LP_HAS_NO_HORN.and(AmuletSelectors.ALICORN_AMULET.or(EquinePredicates.raceMatches(Race::canCast)));
|
||||
public static final Identifier UNICORN_HORN = Unicopia.id("textures/models/horn/unicorn.png");
|
||||
|
||||
public static final Predicate<LivingEntity> PEGA_WINGS_PREDICATE = MINE_LP_HAS_NO_WINGS.and(AmuletSelectors.PEGASUS_AMULET.or(EquinePredicates.raceMatches(Race::canInteractWithClouds)));
|
||||
public static final Predicate<LivingEntity> PEGA_WINGS_PREDICATE = MINE_LP_HAS_NO_WINGS.and(AmuletSelectors.PEGASUS_AMULET.or(EquinePredicates.raceMatches(race -> race.flightType() == FlightType.AVIAN)));
|
||||
public static final Identifier PEGASUS_WINGS = Unicopia.id("textures/models/wings/pegasus_pony.png");
|
||||
|
||||
public static BodyPartGear<WingsGearModel> pegasusWings() {
|
||||
|
|
|
@ -54,9 +54,9 @@ public class Main extends MineLPDelegate implements ClientModInitializer {
|
|||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.CHANGEDLING, Race.CHANGELING);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.ZEBRA, Race.EARTH);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.GRYPHON, Race.PEGASUS);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.HIPPOGRIFF, Race.PEGASUS);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.HIPPOGRIFF, Race.HIPPOGRIFF);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.BATPONY, Race.BAT);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.SEAPONY, Race.UNICORN);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.SEAPONY, Race.SEAPONY);
|
||||
}
|
||||
|
||||
private void onPonyModelPrepared(Entity entity, PonyModel<?> model, ModelAttributes.Mode mode) {
|
||||
|
|
|
@ -24,9 +24,11 @@ public class AccessoryFeatureRenderer<
|
|||
|
||||
private static final List<FeatureFactory<?>> REGISTRY = new ArrayList<>();
|
||||
|
||||
public static <T extends LivingEntity> void register(FeatureFactory<T> factory) {
|
||||
public static void register(FeatureFactory<?>...factories) {
|
||||
for (var factory : factories) {
|
||||
REGISTRY.add(factory);
|
||||
}
|
||||
}
|
||||
|
||||
private final Iterable<Feature<T>> features;
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ public class ModelPartHooks {
|
|||
|
||||
final var bestCandidate = new EnqueudHeadRender();
|
||||
|
||||
matrices.push();
|
||||
part.forEachCuboid(matrices, (entry, name, index, cube) -> {
|
||||
float x = cube.maxX - cube.minX;
|
||||
float y = cube.maxY - cube.minY;
|
||||
|
@ -47,7 +46,6 @@ public class ModelPartHooks {
|
|||
bestCandidate.maxSideLength = Math.max(Math.max(x, z), y);
|
||||
}
|
||||
});
|
||||
matrices.pop();
|
||||
|
||||
if (bestCandidate.transformation != null) {
|
||||
head.add(bestCandidate);
|
||||
|
@ -75,8 +73,6 @@ public class ModelPartHooks {
|
|||
|
||||
matrices.translate(x * PIXEL_SCALE, y * PIXEL_SCALE, z * PIXEL_SCALE);
|
||||
matrices.scale(scale, scale, scale);
|
||||
//matrices.peek().getPositionMatrix().scaleAround(scale, x * PIXEL_SCALE, y * PIXEL_SCALE, z * PIXEL_SCALE);
|
||||
//matrices.translate(cube.minX * PIXEL_SCALE, cube.minY * PIXEL_SCALE, cube.minZ * PIXEL_SCALE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,33 +4,40 @@ import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
|
|||
import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry.DynamicItemRenderer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.item.ClampedModelPredicateProvider;
|
||||
import net.minecraft.client.item.ModelPredicateProviderRegistry;
|
||||
import net.minecraft.client.model.*;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.model.TridentEntityModel;
|
||||
import net.minecraft.client.render.item.ItemRenderer;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.json.ModelTransformationMode;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
import net.minecraft.registry.Registries;
|
||||
|
||||
public class PolearmRenderer implements DynamicItemRenderer {
|
||||
public class PolearmRenderer implements DynamicItemRenderer, ClampedModelPredicateProvider {
|
||||
|
||||
private static final PolearmRenderer INSTANCE = new PolearmRenderer();
|
||||
private static final Identifier THROWING = new Identifier("throwing");
|
||||
|
||||
private final TridentEntityModel model = new TridentEntityModel(getTexturedModelData().createModel());
|
||||
private final ModelPart model = getTexturedModelData().createModel();
|
||||
|
||||
public static void register(Item item) {
|
||||
public static void register(Item...items) {
|
||||
for (Item item : items) {
|
||||
BuiltinItemRendererRegistry.INSTANCE.register(item, INSTANCE);
|
||||
ModelPredicateProviderRegistry.register(item, THROWING, (stack, world, entity, seed) -> {
|
||||
return entity != null && entity.isUsingItem() && entity.getActiveItem() == stack ? 1 : 0;
|
||||
ModelPredicateProviderRegistry.register(item, THROWING, INSTANCE);
|
||||
}
|
||||
ModelLoadingPlugin.register(context -> {
|
||||
for (Item item : items) {
|
||||
context.addModels(getModelId(item));
|
||||
}
|
||||
});
|
||||
ModelLoadingPlugin.register(context -> context.addModels(getModelId(item)));
|
||||
}
|
||||
|
||||
static ModelIdentifier getModelId(ItemConvertible item) {
|
||||
|
@ -50,6 +57,11 @@ public class PolearmRenderer implements DynamicItemRenderer {
|
|||
return TexturedModelData.of(data, 32, 32);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float unclampedCall(ItemStack stack, ClientWorld world, LivingEntity entity, int seed) {
|
||||
return entity != null && entity.isUsingItem() && entity.getActiveItem() == stack ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ItemStack stack, ModelTransformationMode mode, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
|
||||
|
||||
|
@ -77,7 +89,7 @@ public class PolearmRenderer implements DynamicItemRenderer {
|
|||
}
|
||||
Identifier id = Registries.ITEM.getId(stack.getItem());
|
||||
Identifier texture = new Identifier(id.getNamespace(), "textures/entity/polearm/" + id.getPath() + ".png");
|
||||
model.render(matrices, ItemRenderer.getDirectItemGlintConsumer(vertexConsumers, model.getLayer(texture), false, stack.hasGlint()), light, overlay, 1, 1, 1, 1);
|
||||
model.render(matrices, ItemRenderer.getDirectItemGlintConsumer(vertexConsumers, RenderLayer.getEntitySolid(texture), false, stack.hasGlint()), light, overlay, 1, 1, 1, 1);
|
||||
matrices.pop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.minelittlepony.unicopia.client.render;
|
||||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
|
||||
import com.minelittlepony.unicopia.entity.Creature;
|
||||
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment;
|
||||
|
||||
|
@ -20,7 +19,6 @@ import net.minecraft.client.util.math.MatrixStack;
|
|||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Box;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
|
||||
public class SmittenEyesRenderer {
|
||||
private static final Identifier TEXTURE = Unicopia.id("textures/entity/smitten_eyes.png");
|
||||
|
@ -42,9 +40,6 @@ public class SmittenEyesRenderer {
|
|||
ModelPartHooks.stopCollecting().forEach(head -> {
|
||||
matrices.push();
|
||||
head.transform(matrices, 0.95F);
|
||||
if (MineLPDelegate.getInstance().getRace(pony.asEntity()).isEquine()) {
|
||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-90), 0, 1.2F, 0);
|
||||
}
|
||||
float scale = 1F + (1.3F + MathHelper.sin(pony.asEntity().age / 3F) * 0.06F);
|
||||
matrices.scale(scale, scale, scale);
|
||||
matrices.translate(0, 0.05F, 0);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.minelittlepony.unicopia.client.render;
|
||||
|
||||
import com.minelittlepony.unicopia.FlightType;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
|
||||
|
@ -50,7 +51,7 @@ public class WingsFeatureRenderer<E extends LivingEntity> implements AccessoryFe
|
|||
}
|
||||
|
||||
protected boolean canRender(E entity) {
|
||||
return entity instanceof PlayerEntity && Pony.of((PlayerEntity)entity).getObservedSpecies().canInteractWithClouds();
|
||||
return entity instanceof PlayerEntity && Pony.of((PlayerEntity)entity).getObservedSpecies().flightType() == FlightType.AVIAN;
|
||||
}
|
||||
|
||||
protected Identifier getTexture(E entity) {
|
||||
|
|
|
@ -4,16 +4,21 @@ import java.util.Optional;
|
|||
|
||||
import com.minelittlepony.client.util.render.RenderLayerUtil;
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
|
||||
import com.minelittlepony.unicopia.client.render.model.SphereModel;
|
||||
import com.minelittlepony.unicopia.entity.Creature;
|
||||
import com.minelittlepony.unicopia.entity.Equine;
|
||||
import com.minelittlepony.unicopia.entity.ItemImpl;
|
||||
import com.minelittlepony.unicopia.entity.Living;
|
||||
import com.minelittlepony.unicopia.entity.duck.LavaAffine;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.*;
|
||||
import net.minecraft.client.render.BackgroundRenderer.FogType;
|
||||
import net.minecraft.client.render.VertexConsumerProvider.Immediate;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.*;
|
||||
|
@ -43,6 +48,20 @@ public class WorldRenderDelegate {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void applyFog(Camera camera, FogType fogType, float viewDistance, boolean thickFog, float tickDelta) {
|
||||
if (camera.getSubmersionType() == CameraSubmersionType.WATER) {
|
||||
if (EquinePredicates.PLAYER_SEAPONY.test(MinecraftClient.getInstance().player)) {
|
||||
RenderSystem.setShaderFogStart(RenderSystem.getShaderFogStart() - 30);
|
||||
RenderSystem.setShaderFogEnd(RenderSystem.getShaderFogEnd() + 190);
|
||||
}
|
||||
}
|
||||
if (camera.getSubmersionType() == CameraSubmersionType.NONE) {
|
||||
if (EquinePredicates.PLAYER_SEAPONY.test(MinecraftClient.getInstance().player)) {
|
||||
RenderSystem.setShaderFogStart(-130);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean beforeEntityRender(Entity entity,
|
||||
double x, double y, double z, float yaw,
|
||||
float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) {
|
||||
|
@ -80,6 +99,27 @@ public class WorldRenderDelegate {
|
|||
smittenEyesRenderer.render(creature, matrices, immediate, light, 0);
|
||||
}
|
||||
|
||||
if (pony instanceof Pony p) {
|
||||
if (p.getCompositeRace().includes(Race.SEAPONY)
|
||||
&& pony.asEntity().isSubmergedInWater()
|
||||
&& MineLPDelegate.getInstance().getPlayerPonyRace(p.asEntity()) != Race.SEAPONY) {
|
||||
|
||||
for (var head : ModelPartHooks.stopCollecting()) {
|
||||
matrices.push();
|
||||
head.transform(matrices, 1F);
|
||||
|
||||
Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
|
||||
RenderLayer layer = RenderLayers.getMagicColored();
|
||||
float scale = 0.9F;
|
||||
|
||||
SphereModel.SPHERE.render(matrices, immediate.getBuffer(layer), light, 0, scale, 0.5F, 0.5F, 0.5F, 0.1F);
|
||||
SphereModel.SPHERE.render(matrices, immediate.getBuffer(layer), light, 0, scale + 0.2F, 0.5F, 0.5F, 0.5F, 0.1F);
|
||||
|
||||
matrices.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pony instanceof ItemImpl || pony instanceof Living) {
|
||||
matrices.pop();
|
||||
|
||||
|
@ -162,6 +202,13 @@ public class WorldRenderDelegate {
|
|||
roll -= 180;
|
||||
}
|
||||
|
||||
if (p.getAcrobatics().isFloppy()) {
|
||||
matrices.translate(0, -0.5, 0);
|
||||
p.asEntity().setBodyYaw(0);
|
||||
p.asEntity().setYaw(0);
|
||||
matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90));
|
||||
}
|
||||
|
||||
matrices.multiply(RotationAxis.NEGATIVE_Y.rotationDegrees(yaw));
|
||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(roll));
|
||||
|
||||
|
@ -169,6 +216,12 @@ public class WorldRenderDelegate {
|
|||
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(diveAngle));
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw));
|
||||
|
||||
if (p.getCompositeRace().includes(Race.SEAPONY)
|
||||
&& pony.asEntity().isSubmergedInWater()
|
||||
&& MineLPDelegate.getInstance().getPlayerPonyRace(p.asEntity()) != Race.SEAPONY) {
|
||||
ModelPartHooks.startCollecting();
|
||||
}
|
||||
} else if (pony instanceof Creature creature && smittenEyesRenderer.isSmitten(creature)) {
|
||||
ModelPartHooks.startCollecting();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.client.render.entity;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.block.FancyBedBlock.SheetPattern;
|
||||
import com.minelittlepony.unicopia.block.cloud.CloudBedBlock;
|
||||
import com.minelittlepony.unicopia.client.render.RenderLayers;
|
||||
|
||||
|
@ -18,6 +19,7 @@ import net.minecraft.client.model.ModelPartBuilder;
|
|||
import net.minecraft.client.model.ModelPartData;
|
||||
import net.minecraft.client.model.ModelTransform;
|
||||
import net.minecraft.client.model.TexturedModelData;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
|
||||
|
@ -30,14 +32,19 @@ import net.minecraft.util.math.RotationAxis;
|
|||
import net.minecraft.world.World;
|
||||
|
||||
public class CloudBedBlockEntityRenderer implements BlockEntityRenderer<CloudBedBlock.Tile> {
|
||||
private static final Identifier TEXTURE = Unicopia.id("textures/entity/cloud_bed/white.png");
|
||||
|
||||
private final ModelPart bedHead;
|
||||
private final ModelPart bedFoot;
|
||||
private final ModelPart bedHead = getHeadTexturedModelData().createModel();
|
||||
private final ModelPart bedFoot = getFootTexturedModelData().createModel();
|
||||
private final ModelPart bedSheetsHead = getSheetsTexturedModelData(0, 3, 13).createModel();
|
||||
private final ModelPart bedSheetsFoot = getSheetsTexturedModelData(22, 0, 15).createModel();
|
||||
|
||||
public CloudBedBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) {
|
||||
this.bedHead = getHeadTexturedModelData().createModel();
|
||||
this.bedFoot = getFootTexturedModelData().createModel();
|
||||
}
|
||||
|
||||
public static TexturedModelData getSheetsTexturedModelData(int v, int y, int height) {
|
||||
ModelData data = new ModelData();
|
||||
ModelPartData root = data.getRoot();
|
||||
root.addChild("main", ModelPartBuilder.create().uv(0, v).cuboid(0, y, 0, 16, height, 6), ModelTransform.NONE);
|
||||
return TexturedModelData.of(data, 64, 64);
|
||||
}
|
||||
|
||||
public static TexturedModelData getHeadTexturedModelData() {
|
||||
|
@ -66,9 +73,19 @@ public class CloudBedBlockEntityRenderer implements BlockEntityRenderer<CloudBed
|
|||
public void render(CloudBedBlock.Tile entity, float f, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
|
||||
@Nullable
|
||||
World world = entity.getWorld();
|
||||
|
||||
CloudBedBlock.SheetPattern pattern = entity.getPattern();
|
||||
|
||||
VertexConsumer buffer = getBuffer(vertices, entity.getBase());
|
||||
if (world == null) {
|
||||
renderModel(matrices, vertices, bedHead, Direction.SOUTH, TEXTURE, light, overlay, false);
|
||||
renderModel(matrices, vertices, bedFoot, Direction.SOUTH, TEXTURE, light, overlay, true);
|
||||
renderModel(matrices, vertices, bedHead, Direction.SOUTH, buffer, light, overlay, false, false);
|
||||
renderModel(matrices, vertices, bedFoot, Direction.SOUTH, buffer, light, overlay, true, false);
|
||||
if (pattern != CloudBedBlock.SheetPattern.NONE) {
|
||||
buffer = getSheetsBuffer(vertices, pattern);
|
||||
renderModel(matrices, vertices, bedSheetsHead, Direction.SOUTH, buffer, light, overlay, false, true);
|
||||
renderModel(matrices, vertices, bedSheetsFoot, Direction.SOUTH, buffer, light, overlay, true, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -77,11 +94,36 @@ public class CloudBedBlockEntityRenderer implements BlockEntityRenderer<CloudBed
|
|||
renderModel(matrices, vertices,
|
||||
state.get(BedBlock.PART) == BedPart.HEAD ? bedHead : bedFoot,
|
||||
state.get(BedBlock.FACING),
|
||||
TEXTURE,
|
||||
buffer,
|
||||
getModelLight(entity, light),
|
||||
overlay,
|
||||
false,
|
||||
false
|
||||
);
|
||||
if (pattern != CloudBedBlock.SheetPattern.NONE) {
|
||||
renderModel(matrices, vertices,
|
||||
state.get(BedBlock.PART) == BedPart.HEAD ? bedSheetsHead : bedSheetsFoot,
|
||||
state.get(BedBlock.FACING),
|
||||
getSheetsBuffer(vertices, pattern),
|
||||
getModelLight(entity, light),
|
||||
overlay,
|
||||
false,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private VertexConsumer getBuffer(VertexConsumerProvider vertices, String base) {
|
||||
Identifier texture = Unicopia.id("textures/entity/bed/" + base + ".png");
|
||||
return vertices.getBuffer(base.equalsIgnoreCase("cloud")
|
||||
? RenderLayers.getEntityTranslucent(texture)
|
||||
: RenderLayers.getEntityCutout(texture)
|
||||
);
|
||||
}
|
||||
|
||||
private VertexConsumer getSheetsBuffer(VertexConsumerProvider vertices, SheetPattern pattern) {
|
||||
Identifier sheetsTexture = Unicopia.id("textures/entity/bed/sheets/" + pattern.asString() + ".png");
|
||||
return vertices.getBuffer(RenderLayers.getEntityCutout(sheetsTexture));
|
||||
}
|
||||
|
||||
private int getModelLight(CloudBedBlock.Tile entity, int worldLight) {
|
||||
|
@ -95,14 +137,18 @@ public class CloudBedBlockEntityRenderer implements BlockEntityRenderer<CloudBed
|
|||
).apply(new LightmapCoordinatesRetriever<>()).get(worldLight);
|
||||
}
|
||||
|
||||
private void renderModel(MatrixStack matrices, VertexConsumerProvider vertices, ModelPart part, Direction direction, Identifier texture, int light, int overlay, boolean translate) {
|
||||
private void renderModel(MatrixStack matrices, VertexConsumerProvider vertices, ModelPart part, Direction direction, VertexConsumer buffer, int light, int overlay, boolean translate, boolean sheets) {
|
||||
matrices.push();
|
||||
matrices.translate(0, 0.5625f, translate ? -1 : 0);
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
|
||||
matrices.translate(0.5f, 0.5f, 0.5f);
|
||||
if (sheets) {
|
||||
float beddingScale = 1.002F;
|
||||
matrices.scale(beddingScale, beddingScale, beddingScale);
|
||||
}
|
||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180 + direction.asRotation()));
|
||||
matrices.translate(-0.5f, -0.5f, -0.5f);
|
||||
part.render(matrices, vertices.getBuffer(RenderLayers.getEntityTranslucent(texture)), light, overlay);
|
||||
part.render(matrices, buffer, light, overlay);
|
||||
matrices.pop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
package com.minelittlepony.unicopia.client.render.entity;
|
||||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import net.minecraft.block.AbstractChestBlock;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.ChestBlock;
|
||||
import net.minecraft.block.DoubleBlockProperties;
|
||||
import net.minecraft.block.entity.ChestBlockEntity;
|
||||
import net.minecraft.block.enums.ChestType;
|
||||
import net.minecraft.client.model.Dilation;
|
||||
import net.minecraft.client.model.ModelData;
|
||||
import net.minecraft.client.model.ModelPart;
|
||||
import net.minecraft.client.model.ModelPartBuilder;
|
||||
import net.minecraft.client.model.ModelPartData;
|
||||
import net.minecraft.client.model.ModelTransform;
|
||||
import net.minecraft.client.model.TexturedModelData;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory.Context;
|
||||
import net.minecraft.client.render.block.entity.ChestBlockEntityRenderer;
|
||||
import net.minecraft.client.render.block.entity.LightmapCoordinatesRetriever;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
|
||||
public class CloudChestBlockEntityRenderer extends ChestBlockEntityRenderer<ChestBlockEntity> {
|
||||
private static final LightmapCoordinatesRetriever<ChestBlockEntity> LIGHTING = new LightmapCoordinatesRetriever<>();
|
||||
private final Model[] models;
|
||||
|
||||
public CloudChestBlockEntityRenderer(Context ctx) {
|
||||
super(ctx);
|
||||
models = new Model[] {
|
||||
new Model(Model.getSingleChestModelData().createModel(), Unicopia.id("textures/entity/chest/cloud.png")),
|
||||
new Model(Model.getLeftChestModelData().createModel(), Unicopia.id("textures/entity/chest/cloud_left.png")),
|
||||
new Model(Model.getRightChestModelData().createModel(), Unicopia.id("textures/entity/chest/cloud_right.png"))
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ChestBlockEntity entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
|
||||
BlockState state = entity.getWorld() != null ? entity.getCachedState() : Blocks.CHEST.getDefaultState().with(ChestBlock.FACING, Direction.SOUTH);
|
||||
|
||||
if (!(state.getBlock() instanceof AbstractChestBlock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Model model = models[state.getOrEmpty(ChestBlock.CHEST_TYPE).orElse(ChestType.SINGLE).ordinal()];
|
||||
var properties = getProperties(state, entity);
|
||||
|
||||
matrices.push();
|
||||
matrices.translate(0.5f, 0.5f, 0.5f);
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-state.get(ChestBlock.FACING).asRotation()));
|
||||
matrices.translate(-0.5f, -0.5f, -0.5f);
|
||||
model.setAngles(1 - (float)Math.pow(1 - properties.apply(ChestBlock.getAnimationProgressRetriever(entity)).get(tickDelta), 3));
|
||||
model.render(matrices, vertexConsumers.getBuffer(RenderLayer.getEntityTranslucent(model.texture)), properties.apply(LIGHTING).applyAsInt(light), overlay);
|
||||
matrices.pop();
|
||||
}
|
||||
|
||||
private DoubleBlockProperties.PropertySource<? extends ChestBlockEntity> getProperties(BlockState state, ChestBlockEntity entity) {
|
||||
return entity.getWorld() != null
|
||||
? ((AbstractChestBlock<?>)state.getBlock()).getBlockEntitySource(state, entity.getWorld(), entity.getPos(), true)
|
||||
: DoubleBlockProperties.PropertyRetriever::getFallback;
|
||||
}
|
||||
|
||||
static class Model {
|
||||
private final ModelPart tree;
|
||||
private final ModelPart lid;
|
||||
|
||||
private final Identifier texture;
|
||||
|
||||
public Model(ModelPart tree, Identifier texture) {
|
||||
this.tree = tree;
|
||||
this.lid = tree.getChild("lid");
|
||||
this.texture = texture;
|
||||
}
|
||||
|
||||
public static TexturedModelData getSingleChestModelData() {
|
||||
ModelData data = new ModelData();
|
||||
ModelPartData root = data.getRoot();
|
||||
root.addChild("chest", ModelPartBuilder.create().uv(0, 19).cuboid(1, 0, 1, 14, 10, 14, Dilation.NONE), ModelTransform.NONE);
|
||||
root.addChild("lid", ModelPartBuilder.create()
|
||||
.uv(0, 0).cuboid(6, -2, 13.8F, 2, 4, 1, Dilation.NONE)
|
||||
.uv(0, 0).cuboid(6, -1, 14, 2, 2, 1, Dilation.NONE)
|
||||
.uv(0, 0).cuboid(0, 0, 0, 14, 5, 14, new Dilation(0.3F)), ModelTransform.pivot(1, 9, 1))
|
||||
.addChild("lock_r1", ModelPartBuilder.create()
|
||||
.uv(0, 0).cuboid(-2, -4, -0.5F, 2, 4, 1, Dilation.NONE), ModelTransform.of(5, 1, 14.3F, 0, 0, 1.5708F));
|
||||
return TexturedModelData.of(data, 64, 64);
|
||||
}
|
||||
|
||||
public static TexturedModelData getLeftChestModelData() {
|
||||
ModelData data = new ModelData();
|
||||
ModelPartData root = data.getRoot();
|
||||
root.addChild("chest", ModelPartBuilder.create().uv(0, 19).cuboid(0, 0, 1, 15, 10, 14, Dilation.NONE), ModelTransform.NONE);
|
||||
root.addChild("lid", ModelPartBuilder.create()
|
||||
.uv(0, 0).cuboid(6, -2, 13.8F, 2, 4, 1, Dilation.NONE)
|
||||
.uv(0, 0).cuboid(6, -1, 14, 2, 2, 1, Dilation.NONE)
|
||||
.uv(0, 0).cuboid(0, 0, 0, 15, 5, 14, new Dilation(0.3F)), ModelTransform.pivot(0, 9, 1))
|
||||
.addChild("lock_r1", ModelPartBuilder.create().uv(0, 0).cuboid(-2, -4, -0.5F, 2, 4, 1, Dilation.NONE), ModelTransform.of(5, 1, 14.3F, 0, 0, 1.5708F));
|
||||
return TexturedModelData.of(data, 64, 64);
|
||||
}
|
||||
|
||||
public static TexturedModelData getRightChestModelData() {
|
||||
ModelData data = new ModelData();
|
||||
ModelPartData root = data.getRoot();
|
||||
root.addChild("chest", ModelPartBuilder.create().uv(0, 19).cuboid(1, 0, 1, 15, 10, 14, Dilation.NONE), ModelTransform.NONE);
|
||||
root.addChild("lid", ModelPartBuilder.create()
|
||||
.uv(0, 0).cuboid(7, -2, 13.8F, 2, 4, 1, Dilation.NONE)
|
||||
.uv(0, 0).cuboid(7, -1, 14, 2, 2, 1, Dilation.NONE)
|
||||
.uv(0, 0).cuboid(0, 0, 0, 15, 5, 14, new Dilation(0.3F)), ModelTransform.pivot(1, 9, 1))
|
||||
.addChild("lock_r1", ModelPartBuilder.create().uv(0, 0).cuboid(-2, -4, -0.5F, 2, 4, 1, Dilation.NONE), ModelTransform.of(6, 1, 14.3F, 0, 0, 1.5708F));
|
||||
return TexturedModelData.of(data, 64, 64);
|
||||
}
|
||||
|
||||
public void setAngles(float animationProgress) {
|
||||
lid.pitch = -(animationProgress * 1.5707964f);
|
||||
}
|
||||
|
||||
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay) {
|
||||
tree.render(matrices, vertices, light, overlay);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.minelittlepony.unicopia.client.render.entity;
|
||||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
|
||||
import net.minecraft.client.render.entity.EntityRendererFactory.Context;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.mob.SilverfishEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.client.render.entity.SilverfishEntityRenderer;
|
||||
|
||||
public class LootBugEntityRenderer extends SilverfishEntityRenderer {
|
||||
private static final Identifier TEXTURE = Unicopia.id("textures/entity/loot_bug.png");
|
||||
|
||||
public LootBugEntityRenderer(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getTexture(SilverfishEntity entity) {
|
||||
return TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scale(SilverfishEntity entity, MatrixStack matrices, float tickDelta) {
|
||||
float scale = 2;
|
||||
matrices.scale(scale, scale, scale);
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ import com.minelittlepony.unicopia.network.MsgTribeSelect;
|
|||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
|
||||
import net.minecraft.command.argument.EntityArgumentType;
|
||||
import net.minecraft.command.argument.RegistryKeyArgumentType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.CommandManager.RegistrationEnvironment;
|
||||
|
@ -32,8 +31,6 @@ class SpeciesCommand {
|
|||
}
|
||||
}
|
||||
|
||||
RegistryKeyArgumentType<Race> raceArgument = Race.argument();
|
||||
|
||||
return builder
|
||||
.then(CommandManager.literal("get")
|
||||
.executes(context -> get(context.getSource(), context.getSource().getPlayer(), true))
|
||||
|
@ -41,13 +38,13 @@ class SpeciesCommand {
|
|||
.executes(context -> get(context.getSource(), EntityArgumentType.getPlayer(context, "target"), false))
|
||||
))
|
||||
.then(CommandManager.literal("set")
|
||||
.then(CommandManager.argument("race", raceArgument)
|
||||
.then(CommandManager.argument("race", Race.argument())
|
||||
.executes(context -> set(context.getSource(), context.getSource().getPlayer(), Race.fromArgument(context, "race"), true))
|
||||
.then(CommandManager.argument("target", EntityArgumentType.player())
|
||||
.executes(context -> set(context.getSource(), EntityArgumentType.getPlayer(context, "target"), Race.fromArgument(context, "race"), false)))
|
||||
))
|
||||
.then(CommandManager.literal("describe")
|
||||
.then(CommandManager.argument("race", raceArgument)
|
||||
.then(CommandManager.argument("race", Race.argument())
|
||||
.executes(context -> describe(context.getSource().getPlayer(), Race.fromArgument(context, "race")))
|
||||
))
|
||||
.then(CommandManager.literal("list")
|
||||
|
@ -101,7 +98,7 @@ class SpeciesCommand {
|
|||
|
||||
boolean first = true;
|
||||
for (Race i : Race.REGISTRY) {
|
||||
if (!i.isUnset() && i.isPermitted(player)) {
|
||||
if (i.availability().isGrantable() && !i.isUnset() && i.isPermitted(player)) {
|
||||
message.append(Text.literal((!first ? "\n" : "") + " - "));
|
||||
message.append(i.getDisplayName());
|
||||
first = false;
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellEnhancingRe
|
|||
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellShapedCraftingRecipe;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.minelittlepony.unicopia.block.UBlocks;
|
||||
import com.minelittlepony.unicopia.item.EnchantableItem;
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
import com.minelittlepony.unicopia.item.URecipes;
|
||||
|
@ -22,6 +23,7 @@ import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
|||
import dev.emi.emi.api.render.EmiTexture;
|
||||
import dev.emi.emi.api.stack.Comparison;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import dev.emi.emi.recipe.EmiStonecuttingRecipe;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.recipe.RecipeType;
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
|
@ -30,7 +32,9 @@ import net.minecraft.util.Identifier;
|
|||
|
||||
public class Main implements EmiPlugin {
|
||||
static final EmiStack SPELL_BOOK_STATION = EmiStack.of(UItems.SPELLBOOK);
|
||||
static final EmiStack CLOUD_SHAPING_STATION = EmiStack.of(UBlocks.SHAPING_BENCH);
|
||||
static final EmiRecipeCategory SPELL_BOOK_CATEGORY = new EmiRecipeCategory(Unicopia.id("spellbook"), SPELL_BOOK_STATION, SPELL_BOOK_STATION);
|
||||
static final EmiRecipeCategory CLOUD_SHAPING_CATEGORY = new EmiRecipeCategory(Unicopia.id("cloud_shaping"), CLOUD_SHAPING_STATION, CLOUD_SHAPING_STATION);
|
||||
|
||||
static final Identifier WIDGETS = Unicopia.id("textures/gui/widgets.png");
|
||||
static final EmiTexture EMPTY_ARROW = new EmiTexture(WIDGETS, 44, 0, 24, 17);
|
||||
|
@ -70,6 +74,17 @@ public class Main implements EmiPlugin {
|
|||
}
|
||||
});
|
||||
|
||||
registry.addCategory(CLOUD_SHAPING_CATEGORY);
|
||||
registry.addWorkstation(CLOUD_SHAPING_CATEGORY, CLOUD_SHAPING_STATION);
|
||||
registry.getRecipeManager().listAllOfType(URecipes.CLOUD_SHAPING).forEach(recipe -> {
|
||||
registry.addRecipe(new EmiStonecuttingRecipe(recipe.value()) {
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return CLOUD_SHAPING_CATEGORY;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Stream.of(UItems.GEMSTONE, UItems.BOTCHED_GEM, UItems.MAGIC_STAFF, UItems.FILLED_JAR).forEach(item -> {
|
||||
registry.setDefaultComparison(item, comparison -> Comparison.compareNbt());
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.util.stream.Stream;
|
|||
|
||||
import com.minelittlepony.unicopia.EntityConvertable;
|
||||
import com.minelittlepony.unicopia.container.SpellbookScreenHandler;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
|
@ -67,7 +68,7 @@ public interface TrinketsDelegate {
|
|||
}
|
||||
|
||||
default Set<Identifier> getAvailableTrinketSlots(LivingEntity entity, Set<Identifier> probedSlots) {
|
||||
return probedSlots.stream().filter(slot -> getEquipped(entity, slot).count() == 0).collect(Collectors.toSet());
|
||||
return probedSlots.stream().filter(slot -> getEquipped(entity, slot).anyMatch(ItemStack::isEmpty)).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
default Stream<ItemStack> getEquipped(LivingEntity entity, Identifier slot) {
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package com.minelittlepony.unicopia.container;
|
||||
|
||||
import com.minelittlepony.unicopia.block.UBlocks;
|
||||
import com.minelittlepony.unicopia.item.URecipes;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.inventory.SimpleInventory;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.screen.ScreenHandlerContext;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
import net.minecraft.screen.StonecutterScreenHandler;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ShapingBenchScreenHandler extends StonecutterScreenHandler {
|
||||
|
||||
private final ScreenHandlerContext context;
|
||||
private final World world;
|
||||
|
||||
private ItemStack inputStack = ItemStack.EMPTY;
|
||||
|
||||
public ShapingBenchScreenHandler(int syncId, PlayerInventory playerInventory, ScreenHandlerContext context) {
|
||||
super(syncId, playerInventory, context);
|
||||
this.context = context;
|
||||
this.world = playerInventory.player.getWorld();
|
||||
}
|
||||
|
||||
public ShapingBenchScreenHandler(int syncId, PlayerInventory playerInventory) {
|
||||
super(syncId, playerInventory);
|
||||
this.context = ScreenHandlerContext.EMPTY;
|
||||
this.world = playerInventory.player.getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScreenHandlerType<?> getType() {
|
||||
return UScreenHandlers.SHAPING_BENCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(PlayerEntity player) {
|
||||
return canUse(context, player, UBlocks.SHAPING_BENCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentChanged(Inventory inventory) {
|
||||
ItemStack stack = slots.get(0).getStack();
|
||||
if (!stack.isOf(inputStack.getItem())) {
|
||||
inputStack = stack.copy();
|
||||
getAvailableRecipes().clear();
|
||||
setProperty(0, -1);
|
||||
slots.get(1).setStackNoCallbacks(ItemStack.EMPTY);
|
||||
if (!stack.isEmpty()) {
|
||||
getAvailableRecipes().addAll(world.getRecipeManager().getAllMatches(URecipes.CLOUD_SHAPING, input, world));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMove(PlayerEntity player, int slot) {
|
||||
ItemStack originalStack = ItemStack.EMPTY;
|
||||
Slot srcSlot = slots.get(slot);
|
||||
if (srcSlot != null && srcSlot.hasStack()) {
|
||||
ItemStack movingStack = srcSlot.getStack();
|
||||
Item item = movingStack.getItem();
|
||||
originalStack = movingStack.copy();
|
||||
if (slot == 1) {
|
||||
item.onCraft(movingStack, player.getWorld(), player);
|
||||
if (!insertItem(movingStack, 2, 38, true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
srcSlot.onQuickTransfer(movingStack, originalStack);
|
||||
} else if (slot == 0
|
||||
? !insertItem(movingStack, 2, 38, false)
|
||||
: (world.getRecipeManager().getFirstMatch(URecipes.CLOUD_SHAPING, new SimpleInventory(movingStack), world).isPresent()
|
||||
? !insertItem(movingStack, 0, 1, false)
|
||||
: (slot >= 2 && slot < 29
|
||||
? !insertItem(movingStack, 29, 38, false)
|
||||
: slot >= 29 && slot < 38 && !insertItem(movingStack, 2, 29, false)))) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
if (movingStack.isEmpty()) {
|
||||
srcSlot.setStack(ItemStack.EMPTY);
|
||||
}
|
||||
srcSlot.markDirty();
|
||||
if (movingStack.getCount() == originalStack.getCount()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
srcSlot.onTakeItem(player, movingStack);
|
||||
this.sendContentUpdates();
|
||||
}
|
||||
return originalStack;
|
||||
}
|
||||
}
|
|
@ -6,10 +6,12 @@ import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType;
|
|||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.resource.featuretoggle.FeatureFlags;
|
||||
import net.minecraft.registry.Registries;
|
||||
|
||||
public interface UScreenHandlers {
|
||||
ScreenHandlerType<SpellbookScreenHandler> SPELL_BOOK = register("spell_book", new ExtendedScreenHandlerType<>(SpellbookScreenHandler::new));
|
||||
ScreenHandlerType<ShapingBenchScreenHandler> SHAPING_BENCH = register("shaping_bench", new ScreenHandlerType<>(ShapingBenchScreenHandler::new, FeatureFlags.VANILLA_FEATURES));
|
||||
|
||||
static <T extends ScreenHandler> ScreenHandlerType<T> register(String name, ScreenHandlerType<T> type) {
|
||||
return Registry.register(Registries.SCREEN_HANDLER, Unicopia.id(name), type);
|
||||
|
|
22
src/main/java/com/minelittlepony/unicopia/diet/Ailment.java
Normal file
22
src/main/java/com/minelittlepony/unicopia/diet/Ailment.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
package com.minelittlepony.unicopia.diet;
|
||||
|
||||
import com.minelittlepony.unicopia.diet.affliction.Affliction;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
public record Ailment(Affliction effects) {
|
||||
public static final Ailment EMPTY = new Ailment(Affliction.EMPTY);
|
||||
public static final Codec<Ailment> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
Affliction.CODEC.fieldOf("effects").forGetter(Ailment::effects)
|
||||
).apply(instance, Ailment::new));
|
||||
|
||||
public Ailment(PacketByteBuf buffer) {
|
||||
this(Affliction.read(buffer));
|
||||
}
|
||||
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
Affliction.write(buffer, effects);
|
||||
}
|
||||
}
|
157
src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
Normal file
157
src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
Normal file
|
@ -0,0 +1,157 @@
|
|||
package com.minelittlepony.unicopia.diet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.item.ItemDuck;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import net.minecraft.client.item.TooltipContext;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.FoodComponent;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.UseAction;
|
||||
|
||||
public record DietProfile(
|
||||
float defaultMultiplier,
|
||||
float foragingMultiplier,
|
||||
List<Multiplier> multipliers,
|
||||
List<Effect> effects,
|
||||
Optional<Effect> defaultEffect
|
||||
) {
|
||||
public static final DietProfile EMPTY = new DietProfile(1, 1, List.of(), List.of(), Optional.empty());
|
||||
public static final Codec<DietProfile> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
Codec.FLOAT.fieldOf("default_multiplier").forGetter(DietProfile::defaultMultiplier),
|
||||
Codec.FLOAT.fieldOf("foraging_multiplier").forGetter(DietProfile::foragingMultiplier),
|
||||
Codec.list(Multiplier.CODEC).fieldOf("multipliers").forGetter(DietProfile::multipliers),
|
||||
Codec.list(Effect.CODEC).fieldOf("effects").forGetter(DietProfile::effects),
|
||||
Effect.CODEC.optionalFieldOf("default_effect").forGetter(DietProfile::defaultEffect)
|
||||
).apply(instance, DietProfile::new));
|
||||
|
||||
public DietProfile(PacketByteBuf buffer) {
|
||||
this(buffer.readFloat(), buffer.readFloat(), buffer.readList(Multiplier::new), buffer.readList(Effect::new), buffer.readOptional(Effect::new));
|
||||
}
|
||||
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeFloat(defaultMultiplier);
|
||||
buffer.writeFloat(foragingMultiplier);
|
||||
buffer.writeCollection(multipliers, (b, t) -> t.toBuffer(b));
|
||||
buffer.writeCollection(effects, (b, t) -> t.toBuffer(b));
|
||||
buffer.writeOptional(defaultEffect, (b, t) -> t.toBuffer(b));
|
||||
}
|
||||
|
||||
public Optional<Multiplier> findMultiplier(ItemStack stack) {
|
||||
return multipliers.stream().filter(m -> m.test(stack)).findFirst();
|
||||
}
|
||||
|
||||
public Optional<Effect> findEffect(ItemStack stack) {
|
||||
return effects.stream().filter(m -> m.test(stack)).findFirst().or(this::defaultEffect);
|
||||
}
|
||||
|
||||
static boolean isForaged(ItemStack stack) {
|
||||
return ((ItemDuck)stack.getItem()).getOriginalFoodComponent().isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FoodComponent getAdjustedFoodComponent(ItemStack stack) {
|
||||
var food = stack.getItem().getFoodComponent();
|
||||
if (this == EMPTY) {
|
||||
return food;
|
||||
}
|
||||
|
||||
var ratios = getRatios(stack);
|
||||
if (isInedible(ratios)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return FoodAttributes.copy(food)
|
||||
.hunger(Math.max(1, (int)(food.getHunger() * ratios.getFirst())))
|
||||
.saturationModifier(food.getSaturationModifier() * ratios.getSecond())
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean isInedible(ItemStack stack) {
|
||||
return isInedible(getRatios(stack));
|
||||
}
|
||||
|
||||
public boolean isInedible(Pair<Float, Float> ratios) {
|
||||
return ratios.getFirst() <= 0.01F && ratios.getSecond() <= 0.01F;
|
||||
}
|
||||
|
||||
public Pair<Float, Float> getRatios(ItemStack stack) {
|
||||
Optional<Multiplier> multiplier = findMultiplier(stack);
|
||||
|
||||
float baseMultiplier = (isForaged(stack) ? foragingMultiplier() : defaultMultiplier());
|
||||
float hungerMultiplier = multiplier.map(Multiplier::hunger).orElse(baseMultiplier);
|
||||
float saturationMultiplier = multiplier.map(Multiplier::saturation).orElse(baseMultiplier);
|
||||
return Pair.of(hungerMultiplier, saturationMultiplier);
|
||||
}
|
||||
|
||||
public void appendTooltip(ItemStack stack, @Nullable PlayerEntity user, List<Text> tooltip, TooltipContext context) {
|
||||
var food = stack.getItem().getFoodComponent();
|
||||
var ratios = getRatios(stack);
|
||||
if (food == null || isInedible(ratios)) {
|
||||
if (stack.getUseAction() != UseAction.DRINK) {
|
||||
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.not_edible")).formatted(Formatting.DARK_GRAY));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float baseMultiplier = (isForaged(stack) ? foragingMultiplier() : defaultMultiplier());
|
||||
|
||||
if (context.isAdvanced()) {
|
||||
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.base_multiplier", baseMultiplier).formatted(Formatting.DARK_GRAY)));
|
||||
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.hunger.detailed", Math.max(1, (int)(ratios.getFirst() * food.getHunger())), food.getHunger(), (int)(ratios.getFirst() * 100))).formatted(Formatting.DARK_GRAY));
|
||||
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.saturation.detailed", String.format("%.2f", ratios.getSecond() * food.getSaturationModifier()), (int)(ratios.getSecond() * 100))).formatted(Formatting.DARK_GRAY));
|
||||
} else {
|
||||
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.hunger", (int)(ratios.getFirst() * 100))).formatted(Formatting.DARK_GRAY));
|
||||
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.saturation", (int)(ratios.getSecond() * 100))).formatted(Formatting.DARK_GRAY));
|
||||
}
|
||||
}
|
||||
|
||||
public record Multiplier(
|
||||
Set<TagKey<Item>> tags,
|
||||
float hunger,
|
||||
float saturation
|
||||
) implements Predicate<ItemStack> {
|
||||
public static final Codec<Set<TagKey<Item>>> TAGS_CODEC = Codec.list(TagKey.unprefixedCodec(RegistryKeys.ITEM)).xmap(
|
||||
l -> l.stream().distinct().collect(Collectors.toSet()),
|
||||
set -> new ArrayList<>(set)
|
||||
);
|
||||
public static final Codec<Multiplier> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
TAGS_CODEC.fieldOf("tags").forGetter(Multiplier::tags),
|
||||
Codec.FLOAT.fieldOf("hunger").forGetter(Multiplier::hunger),
|
||||
Codec.FLOAT.fieldOf("saturation").forGetter(Multiplier::saturation)
|
||||
).apply(instance, Multiplier::new));
|
||||
|
||||
public Multiplier(PacketByteBuf buffer) {
|
||||
this(buffer.readCollection(HashSet::new, p -> TagKey.of(RegistryKeys.ITEM, p.readIdentifier())), buffer.readFloat(), buffer.readFloat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(ItemStack stack) {
|
||||
return tags.stream().anyMatch(tag -> stack.isIn(tag));
|
||||
}
|
||||
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeCollection(tags, (p, t) -> p.writeIdentifier(t.id()));
|
||||
buffer.writeFloat(hunger);
|
||||
buffer.writeFloat(saturation);
|
||||
}
|
||||
}
|
||||
}
|
27
src/main/java/com/minelittlepony/unicopia/diet/DietView.java
Normal file
27
src/main/java/com/minelittlepony/unicopia/diet/DietView.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
package com.minelittlepony.unicopia.diet;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.item.TooltipContext;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface DietView {
|
||||
TypedActionResult<ItemStack> startUsing(ItemStack stack, World world, PlayerEntity user, Hand hand);
|
||||
|
||||
void finishUsing(ItemStack stack, World world, LivingEntity entity);
|
||||
|
||||
void appendTooltip(ItemStack stack, @Nullable PlayerEntity user, List<Text> tooltip, TooltipContext context);
|
||||
|
||||
interface Holder {
|
||||
default DietView getDiets(ItemStack stack) {
|
||||
return PonyDiets.getInstance();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package com.minelittlepony.unicopia.diet;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.util.Resources;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
|
||||
import net.minecraft.resource.JsonDataLoader;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
|
||||
public class DietsLoader implements IdentifiableResourceReloadListener {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final Identifier ID = Unicopia.id("diets");
|
||||
|
||||
@Override
|
||||
public Identifier getFabricId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> reload(Synchronizer sync, ResourceManager manager,
|
||||
Profiler prepareProfiler, Profiler applyProfiler,
|
||||
Executor prepareExecutor, Executor applyExecutor) {
|
||||
|
||||
var dietsLoadTask = loadData(manager, prepareExecutor, "diets/races").thenApplyAsync(data -> {
|
||||
Map<Race, DietProfile> profiles = new HashMap<>();
|
||||
for (var entry : data.entrySet()) {
|
||||
Identifier id = entry.getKey();
|
||||
try {
|
||||
Race.REGISTRY.getOrEmpty(id).ifPresentOrElse(race -> {
|
||||
DietProfile.CODEC.parse(JsonOps.INSTANCE, entry.getValue())
|
||||
.resultOrPartial(error -> LOGGER.error("Could not load diet profile {}: {}", id, error))
|
||||
.ifPresent(profile -> profiles.put(race, profile));
|
||||
}, () -> LOGGER.warn("Skipped diet for unknown race: " + id));
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Could not load diet profile {}", id, t);
|
||||
}
|
||||
}
|
||||
return profiles;
|
||||
}, prepareExecutor);
|
||||
|
||||
var effectsLoadTask = loadData(manager, prepareExecutor, "diets/food_effects").thenApplyAsync(data -> data.entrySet().stream()
|
||||
.map(entry -> {
|
||||
try {
|
||||
return Effect.CODEC.parse(JsonOps.INSTANCE, entry.getValue())
|
||||
.resultOrPartial(error -> LOGGER.error("Could not load food effect {}: {}", entry.getKey(), error));
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Could not load food effects {}", entry.getKey(), t);
|
||||
}
|
||||
return Optional.<Effect>empty();
|
||||
})
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.toList(), prepareExecutor);
|
||||
|
||||
return CompletableFuture.allOf(dietsLoadTask, effectsLoadTask).thenCompose(sync::whenPrepared).thenRunAsync(() -> {
|
||||
PonyDiets.load(new PonyDiets(
|
||||
dietsLoadTask.getNow(Map.of()),
|
||||
effectsLoadTask.getNow(List.of())
|
||||
));
|
||||
}, applyExecutor);
|
||||
}
|
||||
|
||||
private static CompletableFuture<Map<Identifier, JsonElement>> loadData(ResourceManager manager, Executor prepareExecutor, String path) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Map<Identifier, JsonElement> results = new HashMap<>();
|
||||
JsonDataLoader.load(manager, path, Resources.GSON, results);
|
||||
return results;
|
||||
});
|
||||
}
|
||||
}
|
76
src/main/java/com/minelittlepony/unicopia/diet/Effect.java
Normal file
76
src/main/java/com/minelittlepony/unicopia/diet/Effect.java
Normal file
|
@ -0,0 +1,76 @@
|
|||
package com.minelittlepony.unicopia.diet;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import net.minecraft.client.item.TooltipContext;
|
||||
import net.minecraft.item.FoodComponent;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.UseAction;
|
||||
import net.minecraft.util.Util;
|
||||
|
||||
public record Effect(
|
||||
List<TagKey<Item>> tags,
|
||||
Optional<FoodComponent> foodComponent,
|
||||
Ailment ailment
|
||||
) implements Predicate<ItemStack> {
|
||||
public static final Effect EMPTY = new Effect(List.of(), Optional.empty(), Ailment.EMPTY);
|
||||
public static final Codec<Effect> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
TagKey.unprefixedCodec(RegistryKeys.ITEM).listOf().fieldOf("tags").forGetter(Effect::tags),
|
||||
FoodAttributes.CODEC.optionalFieldOf("food_component").forGetter(Effect::foodComponent),
|
||||
Ailment.CODEC.fieldOf("ailment").forGetter(Effect::ailment)
|
||||
).apply(instance, Effect::new));
|
||||
|
||||
public Effect(PacketByteBuf buffer) {
|
||||
this(buffer.readList(b -> TagKey.of(RegistryKeys.ITEM, b.readIdentifier())), buffer.readOptional(FoodAttributes::read), new Ailment(buffer));
|
||||
}
|
||||
|
||||
public void afflict(Pony pony, ItemStack stack) {
|
||||
ailment().effects().afflict(pony.asEntity(), stack);
|
||||
}
|
||||
|
||||
public void appendTooltip(ItemStack stack, List<Text> tooltip, TooltipContext context) {
|
||||
int size = tooltip.size();
|
||||
tags.forEach(tag -> {
|
||||
if (stack.isIn(tag)) {
|
||||
tooltip.add(Text.literal(" ").append(Text.translatable(Util.createTranslationKey("tag", tag.id()))).formatted(Formatting.GRAY));
|
||||
}
|
||||
});
|
||||
if (tooltip.size() == size) {
|
||||
if (stack.isFood()) {
|
||||
tooltip.add(Text.literal(" ").append(Text.translatable("tag.unicopia.food_types.fruits_and_vegetables")).formatted(Formatting.GRAY));
|
||||
} else if (stack.getUseAction() == UseAction.DRINK) {
|
||||
tooltip.add(Text.literal(" ").append(Text.translatable("tag.unicopia.food_types.drinks")).formatted(Formatting.GRAY));
|
||||
}
|
||||
}
|
||||
|
||||
if (context.isAdvanced() && stack.isFood()) {
|
||||
if (!ailment().effects().isEmpty()) {
|
||||
tooltip.add(Text.translatable("unicopia.diet.side_effects").formatted(Formatting.DARK_PURPLE));
|
||||
ailment().effects().appendTooltip(tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeCollection(tags, (b, t) -> b.writeIdentifier(t.id()));
|
||||
buffer.writeOptional(foodComponent, FoodAttributes::write);
|
||||
ailment.toBuffer(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(ItemStack stack) {
|
||||
return tags.stream().anyMatch(stack::isIn);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.minelittlepony.unicopia.diet;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
||||
import net.minecraft.item.FoodComponent;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
final class FoodAttributes {
|
||||
static final Codec<FoodComponent> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
Codec.INT.fieldOf("hunger").forGetter(FoodComponent::getHunger),
|
||||
Codec.FLOAT.fieldOf("saturation").forGetter(FoodComponent::getSaturationModifier),
|
||||
Codec.BOOL.optionalFieldOf("petFood", false).forGetter(FoodComponent::isMeat),
|
||||
Codec.BOOL.optionalFieldOf("fastFood", false).forGetter(FoodComponent::isAlwaysEdible),
|
||||
Codec.BOOL.optionalFieldOf("eatenQuickly", false).forGetter(FoodComponent::isSnack)
|
||||
).apply(instance, FoodAttributes::create));
|
||||
|
||||
static FoodComponent create(int hunger, float saturation, boolean petFood, boolean fastFood, boolean eatenQuickly) {
|
||||
return create(hunger, saturation, petFood, fastFood, eatenQuickly, List.of()).build();
|
||||
}
|
||||
|
||||
static FoodComponent.Builder create(int hunger, float saturation, boolean petFood, boolean fastFood, boolean eatenQuickly, List<Pair<StatusEffectInstance, Float>> effects) {
|
||||
var builder = new FoodComponent.Builder()
|
||||
.hunger(hunger)
|
||||
.saturationModifier(saturation);
|
||||
if (petFood) {
|
||||
builder.meat();
|
||||
}
|
||||
if (fastFood) {
|
||||
builder.alwaysEdible();
|
||||
}
|
||||
if (eatenQuickly) {
|
||||
builder.snack();
|
||||
}
|
||||
for (var effect : effects) {
|
||||
builder.statusEffect(effect.getFirst(), effect.getSecond());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
static FoodComponent.Builder copy(FoodComponent food) {
|
||||
return create(food.getHunger(), food.getSaturationModifier(), food.isMeat(), food.isAlwaysEdible(), food.isSnack(), food.getStatusEffects());
|
||||
}
|
||||
|
||||
static FoodComponent read(PacketByteBuf buffer) {
|
||||
return create(buffer.readInt(), buffer.readFloat(), buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean());
|
||||
}
|
||||
|
||||
static void write(PacketByteBuf buffer, FoodComponent food) {
|
||||
buffer.writeInt(food.getHunger());
|
||||
buffer.writeFloat(food.getSaturationModifier());
|
||||
buffer.writeBoolean(food.isMeat());
|
||||
buffer.writeBoolean(food.isAlwaysEdible());
|
||||
buffer.writeBoolean(food.isSnack());
|
||||
}
|
||||
}
|
109
src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java
Normal file
109
src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java
Normal file
|
@ -0,0 +1,109 @@
|
|||
package com.minelittlepony.unicopia.diet;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.entity.effect.FoodPoisoningStatusEffect;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.item.ItemDuck;
|
||||
import net.minecraft.client.item.TooltipContext;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class PonyDiets implements DietView {
|
||||
private final Map<Race, DietProfile> diets;
|
||||
private final List<Effect> effects;
|
||||
|
||||
private static PonyDiets INSTANCE = new PonyDiets(Map.of(), List.of());
|
||||
|
||||
public static PonyDiets getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static void load(PonyDiets diets) {
|
||||
INSTANCE = diets;
|
||||
}
|
||||
|
||||
PonyDiets(Map<Race, DietProfile> diets, List<Effect> effects) {
|
||||
this.diets = diets;
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
public PonyDiets(PacketByteBuf buffer) {
|
||||
this(buffer.readMap(b -> b.readRegistryValue(Race.REGISTRY), DietProfile::new), buffer.readList(Effect::new));
|
||||
}
|
||||
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeMap(diets, (b, r) -> b.writeRegistryValue(Race.REGISTRY, r), (b, e) -> e.toBuffer(b));
|
||||
buffer.writeCollection(effects, (b, e) -> e.toBuffer(b));
|
||||
}
|
||||
|
||||
private DietProfile getDiet(Pony pony) {
|
||||
return Optional.ofNullable(diets.get(pony.getObservedSpecies())).orElse(DietProfile.EMPTY);
|
||||
}
|
||||
|
||||
private Effect getEffects(ItemStack stack) {
|
||||
return effects.stream().filter(effect -> effect.test(stack)).findFirst().orElse(Effect.EMPTY);
|
||||
}
|
||||
|
||||
private Effect getEffects(ItemStack stack, Pony pony) {
|
||||
return getDiet(pony).findEffect(stack).orElseGet(() -> getEffects(stack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedActionResult<ItemStack> startUsing(ItemStack stack, World world, PlayerEntity user, Hand hand) {
|
||||
return initEdibility(stack, user)
|
||||
? FoodPoisoningStatusEffect.apply(stack, user)
|
||||
: TypedActionResult.fail(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUsing(ItemStack stack, World world, LivingEntity entity) {
|
||||
if (initEdibility(stack, entity)) {
|
||||
Pony.of(entity).ifPresent(pony -> getEffects(stack, pony).afflict(pony, stack));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTooltip(ItemStack stack, @Nullable PlayerEntity user, List<Text> tooltip, TooltipContext context) {
|
||||
if (initEdibility(stack, user)) {
|
||||
Pony pony = Pony.of(user);
|
||||
|
||||
tooltip.add(Text.translatable("unicopia.diet.information").formatted(Formatting.DARK_PURPLE));
|
||||
getEffects(stack, pony).appendTooltip(stack, tooltip, context);
|
||||
getDiet(pony).appendTooltip(stack, user, tooltip, context);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean initEdibility(ItemStack stack, LivingEntity user) {
|
||||
ItemDuck item = (ItemDuck)stack.getItem();
|
||||
item.resetFoodComponent();
|
||||
return Pony.of(user).filter(pony -> {
|
||||
DietProfile diet = getDiet(pony);
|
||||
|
||||
if (!stack.isFood() && pony.getObservedSpecies().hasIronGut()) {
|
||||
diet.findEffect(stack)
|
||||
.flatMap(Effect::foodComponent)
|
||||
.or(() -> getEffects(stack).foodComponent())
|
||||
.ifPresent(item::setFoodComponent);
|
||||
}
|
||||
|
||||
if (stack.isFood()) {
|
||||
item.setFoodComponent(diet.getAdjustedFoodComponent(stack));
|
||||
}
|
||||
|
||||
return true;
|
||||
}).isPresent();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package com.minelittlepony.unicopia.diet.affliction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.dynamic.Codecs;
|
||||
|
||||
public interface Affliction {
|
||||
Affliction EMPTY = new Affliction() {
|
||||
@Override
|
||||
public void afflict(PlayerEntity player, ItemStack stack) { }
|
||||
|
||||
@Override
|
||||
public AfflictionType<?> getType() {
|
||||
return AfflictionType.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBuffer(PacketByteBuf buffer) { }
|
||||
};
|
||||
Codec<Affliction> CODEC = Codecs.xor(AfflictionType.CODEC, Codec.list(AfflictionType.CODEC).xmap(
|
||||
afflictions -> {
|
||||
afflictions = afflictions.stream().filter(f -> !f.isEmpty()).toList();
|
||||
return switch (afflictions.size()) {
|
||||
case 0 -> EMPTY;
|
||||
case 1 -> afflictions.get(0);
|
||||
default -> new CompoundAffliction(afflictions);
|
||||
};
|
||||
},
|
||||
affliction -> ((CompoundAffliction)affliction).afflictions
|
||||
)).xmap(
|
||||
either -> either.left().or(either::right).get(),
|
||||
affliction -> affliction instanceof CompoundAffliction ? Either.left(affliction) : Either.right(affliction)
|
||||
);
|
||||
|
||||
void afflict(PlayerEntity player, ItemStack stack);
|
||||
|
||||
default boolean isEmpty() {
|
||||
return getType() == AfflictionType.EMPTY;
|
||||
}
|
||||
|
||||
default void appendTooltip(List<Text> tooltip) {
|
||||
tooltip.add(Text.literal(" ").append(getName()).formatted(Formatting.DARK_GRAY));
|
||||
}
|
||||
|
||||
default Text getName() {
|
||||
return Text.translatable(getType().getTranslationKey());
|
||||
}
|
||||
|
||||
AfflictionType<?> getType();
|
||||
|
||||
void toBuffer(PacketByteBuf buffer);
|
||||
|
||||
static void write(PacketByteBuf buffer, Affliction affliction) {
|
||||
buffer.writeIdentifier(affliction.getType().id());
|
||||
affliction.toBuffer(buffer);
|
||||
}
|
||||
|
||||
static Affliction read(PacketByteBuf buffer) {
|
||||
return AfflictionType.REGISTRY.get(buffer.readIdentifier()).reader().apply(buffer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.minelittlepony.unicopia.diet.affliction;
|
||||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.util.RegistryUtils;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf.PacketReader;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.JsonHelper;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.dynamic.Codecs;
|
||||
|
||||
public record AfflictionType<T extends Affliction>(Codec<T> codec, Identifier id, PacketReader<T> reader) {
|
||||
public static final String DEFAULT_ID = "unicopia:apply_status_effect";
|
||||
public static final Registry<AfflictionType<?>> REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("affliction_type"), DEFAULT_ID);
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Codec<Affliction> CODEC = Codecs.JSON_ELEMENT.<Affliction>flatXmap(json -> {
|
||||
if (!json.isJsonObject()) {
|
||||
return DataResult.error(() -> "Not a JSON object");
|
||||
}
|
||||
return Identifier.validate(JsonHelper.getString(JsonHelper.asObject(json, "affliction"), "type", AfflictionType.DEFAULT_ID))
|
||||
.flatMap(type -> AfflictionType.REGISTRY.get(type).codec().parse(JsonOps.INSTANCE, json));
|
||||
}, thing -> {
|
||||
AfflictionType<?> type = thing.getType();
|
||||
return ((Codec<Affliction>)type.codec()).encodeStart(JsonOps.INSTANCE, thing).map(json -> {
|
||||
if (json.isJsonObject()) {
|
||||
json.getAsJsonObject().addProperty("type", type.id().toString());
|
||||
}
|
||||
return json;
|
||||
});
|
||||
});
|
||||
|
||||
public static final AfflictionType<Affliction> EMPTY = register("empty", Codec.unit(Affliction.EMPTY), buffer -> Affliction.EMPTY);
|
||||
public static final AfflictionType<Affliction> MANY = register("many", CompoundAffliction.CODEC, CompoundAffliction::new);
|
||||
public static final AfflictionType<StatusEffectAffliction> APPLY_STATUS_EFFECT = register("apply_status_effect", StatusEffectAffliction.CODEC, StatusEffectAffliction::new);
|
||||
public static final AfflictionType<LoseHungerAffliction> LOSE_HUNGER = register("lose_hunger", LoseHungerAffliction.CODEC, LoseHungerAffliction::new);
|
||||
public static final AfflictionType<HealingAffliction> HEALING = register("healing", HealingAffliction.CODEC, HealingAffliction::new);
|
||||
public static final AfflictionType<ClearLoveSicknessAffliction> CURE_LOVE_SICKNESS = register("cure_love_sickness", ClearLoveSicknessAffliction.CODEC, buffer -> ClearLoveSicknessAffliction.INSTANCE);
|
||||
|
||||
static <T extends Affliction> AfflictionType<T> register(String name, Codec<T> codec, PacketReader<T> reader) {
|
||||
return Registry.register(REGISTRY, Unicopia.id(name), new AfflictionType<>(codec, Unicopia.id(name), reader));
|
||||
}
|
||||
|
||||
public String getTranslationKey() {
|
||||
return Util.createTranslationKey("affliction", id());
|
||||
}
|
||||
|
||||
public static void bootstrap() { }
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue