diff --git a/HOW_TO_PLAY.md b/HOW_TO_PLAY.md index c0bb6f37..a29546d9 100644 --- a/HOW_TO_PLAY.md +++ b/HOW_TO_PLAY.md @@ -3,7 +3,7 @@ [![ru](https://img.shields.io/badge/lang-ru-d52b1e.svg)](README_RU.md) [![cn](https://img.shields.io/badge/lang-cn-de2910.svg)](README_CN.md) -When starting a new world you're given the choice of which tribe to join. One of Unicorn, Pegasus, Earth, Bat, or Changeling. +When starting a new world you're given the choice of which tribe to join. You can choose from _Unicorn_, _Pegasus_, _Earth Pony_, _Batpony_, or _Changeling_. Depending on which race you pick, you're given different abilities, displayed on your HUD in a series of circular elements. To activate an ability, simply press and hold the key corresponding to that ability. Some take longer than others, and certain abilities @@ -17,7 +17,7 @@ also respond to quick, short single-taps, or double-taps. For unicorns, casting spells is done through gems, which you can obtain whilst mining. You first need to craft a spellbook using a gem, and then in the spellbook you can discover the magical traits of different items and recipes to combine them to create different spells, as well - as modify existing one. + as modify existing ones. Once you have a gem with a spell you want to use, you can equip it to your main-hand or off-hand slot by right-clicking with a gem in one of either hand, then to activate it you us your primary ability. You can also cast spells directly from a gem by using the ability @@ -34,7 +34,7 @@ also respond to quick, short single-taps, or double-taps. ### Earth Ponies - Kicking & Stomping - Earth ponies kick good. + *Earth ponies kick good*. If Mine Little Pony is installed, and you have the appearance of a pony, kicking will target the block behind you, so twirl that rump! Kicking blocks will incrementally mine them, and kicking trees will shake items loose from their branches. Kicking a tree _too much_ might destroy it, @@ -47,7 +47,7 @@ also respond to quick, short single-taps, or double-taps. - Bracing - Earth ponies can brace themselves by sneaking! It, uh, makes you harder to push! yeah! + Earth ponies can brace themselves by sneaking! It, uh, makes you harder to push! Yeah! ### Pegasi / Bat Ponies @@ -99,7 +99,7 @@ also respond to quick, short single-taps, or double-taps. - Hanging of Ceilings - Ever just want to hang out? Well bat ponies can, _literally_! All the cool kids are doing. + Ever just want to hang out? Well bat ponies can, _literally_! All the cool kids are doing it! - Mangoes @@ -121,6 +121,12 @@ also respond to quick, short single-taps, or double-taps. Changelings can turn into damn near anything, even other players! And blocks! And hostile mobs! Careful about turning into skeletons, though, because they hate the sun even more than bat ponies. + + - Crawling + + Changelings can crawl on Ceilings, which is very useful for lining up your shot, when attempting to shoot somepony in a cave, + while disguised as a stone block. Or is that just me? Anyways, they can only crawl along flat surfaces, meaning that if you go off the edge of a block, + you will fall off. ## Special Items, Plants, Tools @@ -135,7 +141,7 @@ Fighting too close together with them may cause you some knockback. Zap Apple Trees occur naturally in the world and are the only way to obtain zap apples in survival. They can appear in one of several different states: -Hybernating, Flowering, Fruiting, or Withering +*Hibernating, Flowering, Fruiting, or Withering* They cycle through these states throughout the lunar cycle, so if you find one, and it's not in the state you want, wait around another few days and it will eventually bear fruit, but don't try to harvest the apples before they're ripe, because they will zap you! @@ -144,4 +150,6 @@ If you're able to obtain the wood and leaves, it also makes the perfect deterant ### Muffins -They're bouncy and delicious, and pigs absolutely love them! \ No newline at end of file +They're bouncy and delicious, and pigs absolutely love them! + + \ No newline at end of file diff --git a/README.md b/README.md index 05a9d06f..e54b1da3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Bringing the magic of friendship to Minecraft! -What started as a humble utility to make playing as a unicorn a little more emersive has grown into a full-blown pony +What started as a humble utility to make playing as a unicorn a little more immersive has grown into a full-blown pony conversion experience that brings new magic, mechanics and experience to the world of Minecraft to make it truly feel like you've entered the world of Equestria! @@ -24,13 +24,13 @@ Unicorns, Pegasi, Earth Ponies, and even Changelings get their own special abili - *Play as a unicorn* and learn to use magic! Craft your first spellbook and experiment, finding the different spells you can make and what they do, or simply delve into the lore to learn more about the past of this mysterious world! - Besides casting spells, such as a shield to protect themselves, of fire a bolt of magic to incinerate your foes, + Besides casting spells, such as a shield to protect themselves, or a bolt of magic to incinerate your foes, Unicorns can also teleport to get around obstacles or simply reach those hard to reach places. - - *Play as a pegasus* and dominate the skies! Besides the ability to fly, pegasi can also perform rainbooms, control the weather by shoving them into jars, + - *Play as a pegasus* and dominate the skies! Besides the ability to fly, pegasi can also perform sonic rainbooms, control the weather by shoving them into jars, and have a greater reach distance and speed than other races. - - *Play as a humble background pony*! Earth Ponies are tougher and heavier than the other races. They also have the nify ability to + - *Play as a humble background pony*! Earth Ponies are tougher and heavier than the other races. They also have the nifty ability to kick trees to get food and hasten the growth of crops. You'll never go hungry if you're an earth pony. Feeling like going over to the dark side? @@ -58,7 +58,7 @@ Unicorns, Pegasi, Earth Ponies, and even Changelings get their own special abili - Airflow is simulated (badly) Pegasi, beware about flying during storms! It can get dangerous out there! - If you're playing as a flying species, or just like having nice things, try building a weather vein. + If you're playing as a flying species, or just like having nice things, try building a weather vane. It shows the actual, totally real and not simulated badly, wind direction of your minecraft world. Just beware that the direction and strength is situational (and bad), and will be different depending where you are and how high up you are. diff --git a/assets/accretion_disk.svg b/assets/accretion_disk.svg new file mode 100644 index 00000000..ee302045 --- /dev/null +++ b/assets/accretion_disk.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/assets/models/bulb_angry.png b/assets/models/bulb_angry.png new file mode 100644 index 00000000..5f5cc603 Binary files /dev/null and b/assets/models/bulb_angry.png differ diff --git a/assets/models/bulb_idle.png b/assets/models/bulb_idle.png new file mode 100644 index 00000000..7fe663ff Binary files /dev/null and b/assets/models/bulb_idle.png differ diff --git a/assets/models/cloud_bed.java b/assets/models/cloud_bed.java deleted file mode 100644 index 88008846..00000000 --- a/assets/models/cloud_bed.java +++ /dev/null @@ -1,50 +0,0 @@ -// Made with Blockbench 4.8.3 -// Exported for Minecraft version 1.17 or later with Mojang mappings -// Paste this class into your mod and generate all required imports - - -public class cloud_bed extends EntityModel { - // This layer location should be baked with EntityRendererProvider.Context in the entity renderer and passed into this model's constructor - public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(new ResourceLocation("modid", "cloud_bed"), "main"); - private final ModelPart head; - private final ModelPart foot; - - public cloud_bed(ModelPart root) { - this.head = root.getChild("head"); - this.foot = root.getChild("foot"); - } - - public static LayerDefinition createBodyLayer() { - MeshDefinition meshdefinition = new MeshDefinition(); - PartDefinition partdefinition = meshdefinition.getRoot(); - - PartDefinition head = partdefinition.addOrReplaceChild("head", CubeListBuilder.create(), PartPose.offset(0.0F, 24.0F, 0.0F)); - - PartDefinition main_r1 = head.addOrReplaceChild("main_r1", CubeListBuilder.create().texOffs(0, 0).addBox(-8.0F, -21.0F, -9.0F, 16.0F, 13.0F, 6.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, -16.0F, -1.5708F, 0.0F, 0.0F)); - - PartDefinition head_board = head.addOrReplaceChild("head_board", CubeListBuilder.create().texOffs(52, 24).addBox(7.0F, -13.0F, -3.0F, 2.0F, 13.0F, 3.0F, new CubeDeformation(0.0F)) - .texOffs(0, 43).addBox(-6.0F, -15.0F, -2.0F, 13.0F, 15.0F, 3.0F, new CubeDeformation(0.0F)) - .texOffs(52, 24).addBox(-8.0F, -13.0F, -3.0F, 2.0F, 13.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(-0.5F, 0.0F, 7.0F)); - - PartDefinition foot = partdefinition.addOrReplaceChild("foot", CubeListBuilder.create(), PartPose.offset(0.0F, 24.0F, 0.0F)); - - PartDefinition main_r2 = foot.addOrReplaceChild("main_r2", CubeListBuilder.create().texOffs(0, 22).addBox(-8.0F, -8.0F, -9.0F, 16.0F, 15.0F, 6.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, -1.5708F, 0.0F, 0.0F)); - - PartDefinition head_board2 = foot.addOrReplaceChild("head_board2", CubeListBuilder.create().texOffs(52, 40).addBox(6.0F, -13.0F, -2.0F, 3.0F, 10.0F, 3.0F, new CubeDeformation(0.0F)) - .texOffs(0, 43).addBox(-6.0F, -15.0F, -3.0F, 13.0F, 11.0F, 3.0F, new CubeDeformation(0.0F)) - .texOffs(52, 40).addBox(-8.0F, -13.0F, -2.0F, 3.0F, 10.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(-0.5F, 3.0F, -7.0F)); - - return LayerDefinition.create(meshdefinition, 64, 64); - } - - @Override - public void setupAnim(T entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { - - } - - @Override - public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { - head.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); - foot.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); - } -} \ No newline at end of file diff --git a/assets/models/cloud_chest.java b/assets/models/cloud_chest.java deleted file mode 100644 index 87e684a2..00000000 --- a/assets/models/cloud_chest.java +++ /dev/null @@ -1,32 +0,0 @@ -// 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 { - 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); - } -} \ No newline at end of file diff --git a/assets/models/hay_bale.bbmodel b/assets/models/hay_bale.bbmodel new file mode 100644 index 00000000..7328ccde --- /dev/null +++ b/assets/models/hay_bale.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.5","model_format":"java_block","box_uv":false},"name":"hay_bale_bse","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":"bottom_south_east","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[8,0,8],"to":[16,8,16],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[0,8,8,16],"texture":0},"east":{"uv":[0,8,8,16],"texture":1},"south":{"uv":[8,8,16,16],"texture":1},"west":{"uv":[8,8,16,16],"texture":0},"up":{"uv":[8,8,16,16],"texture":0},"down":{"uv":[8,0,16,8],"texture":0}},"type":"cube","uuid":"523f4164-daa5-4b79-eec7-013710b7ae88"},{"name":"bottom_north_west","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,0,0],"to":[8,8,8],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[8,8,16,16],"texture":1},"east":{"uv":[8,8,16,16],"texture":0},"south":{"uv":[0,8,8,16],"texture":0},"west":{"uv":[0,8,8,16],"texture":1},"up":{"uv":[0,0,8,8],"texture":0},"down":{"uv":[0,8,8,16],"texture":0}},"type":"cube","uuid":"1fd288fe-a293-1ca2-a5b7-15d602eb44f6"},{"name":"top_south_east","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[8,8,8],"to":[16,16,16],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,8,8],"texture":0},"east":{"uv":[0,0,8,8],"texture":1},"south":{"uv":[8,0,16,8],"texture":1},"west":{"uv":[8,0,16,8],"texture":0},"up":{"uv":[8,8,16,16],"texture":0},"down":{"uv":[8,0,16,8],"texture":0}},"type":"cube","uuid":"4f48967d-c722-5593-6f77-079ee47d08af"},{"name":"top_north_west","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,8,0],"to":[8,16,8],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[8,0,16,8],"texture":1},"east":{"uv":[8,0,16,8],"texture":0},"south":{"uv":[0,0,8,8],"texture":0},"west":{"uv":[0,0,8,8],"texture":1},"up":{"uv":[0,0,8,8],"texture":0},"down":{"uv":[0,8,8,16],"texture":0}},"type":"cube","uuid":"c4356ab3-a7a5-43e5-489a-9ebb52492675"},{"name":"bottom_south_west","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,0,8],"to":[8,8,16],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[8,8,16,16],"texture":0},"east":{"uv":[0,8,8,16],"texture":0},"south":{"uv":[0,8,8,16],"texture":1},"west":{"uv":[8,8,16,16],"texture":1},"up":{"uv":[0,8,8,16],"texture":0},"down":{"uv":[0,0,8,8],"texture":0}},"type":"cube","uuid":"d07d02d7-5ab9-0026-b3ed-029701536cf6"},{"name":"bottom_north_east","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[8,0,0],"to":[16,8,8],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[0,8,8,16],"texture":1},"east":{"uv":[8,8,16,16],"texture":1},"south":{"uv":[8,8,16,16],"texture":0},"west":{"uv":[0,8,8,16],"texture":0},"up":{"uv":[8,0,16,8],"texture":0},"down":{"uv":[8,8,16,16],"texture":0}},"type":"cube","uuid":"d10fb8d4-fe44-ca10-77f0-63b32855f5d6"},{"name":"top_south_west","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,8,8],"to":[8,16,16],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[8,0,16,8],"texture":0},"east":{"uv":[0,0,8,8],"texture":0},"south":{"uv":[0,0,8,8],"texture":1},"west":{"uv":[8,0,16,8],"texture":1},"up":{"uv":[0,8,8,16],"texture":0},"down":{"uv":[0,0,8,8],"texture":0}},"type":"cube","uuid":"b9c9d695-4003-c9e6-efbb-9a75c658b47e"},{"name":"top_north_east","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[8,8,0],"to":[16,16,8],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,8,8],"texture":1},"east":{"uv":[8,0,16,8],"texture":1},"south":{"uv":[8,0,16,8],"texture":0},"west":{"uv":[0,0,8,8],"texture":0},"up":{"uv":[8,0,16,8],"texture":0},"down":{"uv":[8,8,16,16],"texture":0}},"type":"cube","uuid":"20a14bb3-edf7-1ae1-132e-ef3d56d84f67"}],"outliner":["523f4164-daa5-4b79-eec7-013710b7ae88","4f48967d-c722-5593-6f77-079ee47d08af","d10fb8d4-fe44-ca10-77f0-63b32855f5d6","20a14bb3-edf7-1ae1-132e-ef3d56d84f67","d07d02d7-5ab9-0026-b3ed-029701536cf6","b9c9d695-4003-c9e6-efbb-9a75c658b47e","1fd288fe-a293-1ca2-a5b7-15d602eb44f6","c4356ab3-a7a5-43e5-489a-9ebb52492675"],"textures":[{"path":"/home/sollace/Desktop/hay_block_top.png","name":"hay_block_top.png","folder":"blocks","namespace":"minecraft","id":"top","particle":false,"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":"f73f23e2-d591-dd05-db6f-60869e1592c7","relative_path":"../../../../../../../Desktop/hay_block_top.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAVVJREFUOE9tkyFOBEEQRWvckEAyuBWYFSRgCQgk3IgDcAKC4wo4rgBIxBIsJIg1iHVsAgnjhryevE6xTJvd6ar69ev/6ub+thte3yJmXR+c1bqNn++I+V5f/nuMm8Mvd83VRTccHUd8rvrYnbWRwUhafrQVjAJBDw/Gmubuuh3sMt+PWL5HASKY79df4x2xl+eRIeCFgXRBJXh+1sfTog2+ZSQTm/C9tR3R3Fx2AzPzwZkqMtkRpM84VQNpcUkiRTCRetbi9KSPh8dRmz8aMF+3858+oBQJZpPCABFVX/s2rWJuNNHKPFLRAHTVpxgWdFPA3FEm5FUbcwIBmHDcDxhk+opITmGgaHqrrapO1zyC+1H3gE66QDDPTFLeVGjneBFRilNWyirvCXmAolHdRH3O6wttHw06ObuuVQ1y0ua2EXM/cMojSMNzZi5fWX7KWdwppxjjF8NCOiclFu9xAAAAAElFTkSuQmCC"},{"path":"/home/sollace/Desktop/hay_block_side.png","name":"hay_block_side.png","folder":"blocks","namespace":"minecraft","id":"side","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":"1cf36f16-9f26-3125-d49c-45217088cbc7","relative_path":"../../../../../../../Desktop/hay_block_side.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAV1JREFUOE+VU7tOQkEUPGtiuAokeCExNIpBK2x9tITK7/EHqCmJv2DlNxhblZqORK2MiVxJBLmEhDVzktkclopt9p7X7OzcWde7bfiL6wMZDsfSalVl8Pwj7U5FXl4zjZn//pxKrV5aixunRXF33SNvGzFwdZmuARymBfnKFgpIIBx0crwrDgz2yytBE05AgYyQe/tYCup/vzs6gBg7l7u/Ofc+8ZJNckkrSSgg5ppNF1IsFTbq6NcrkCJPxAmgbPOMyYDMggaxSBTz6XGilGNAArmH/pnH3QEAVa36EPN9NFNtbN2KugGAAQvEwRiIfQqAASa23QMAqJNyDGivEDNUH8TOs+rjV0JALDoVPqHort9uehTpAZc7GedzqSZ7An8wtj34pm+CE5GE2+yiA7Fjxc5EXgFoUTTR1tYw9AEPsdYOTuQAH419E3xs1qnoh3aBgR20otKRVn2+Vuz/aIRXgq2GkfcAAAAASUVORK5CYII="}]} \ No newline at end of file diff --git a/assets/models/hay_bale.json b/assets/models/hay_bale.json new file mode 100644 index 00000000..a2ce1538 --- /dev/null +++ b/assets/models/hay_bale.json @@ -0,0 +1,113 @@ +{ + "textures": { + "top": "blocks/hay_block_top", + "particle": "blocks/hay_block_side", + "side": "blocks/hay_block_side" + }, + "elements": [ + { + "name": "bottom_south_east", + "from": [8, 0, 8], + "to": [16, 8, 16], + "faces": { + "north": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "east": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "south": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "west": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "up": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "down": {"uv": [8, 0, 16, 8], "texture": "#top"} + } + }, + { + "name": "top_south_east", + "from": [8, 8, 8], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "east": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "south": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "up": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "down": {"uv": [8, 0, 16, 8], "texture": "#top"} + } + }, + { + "name": "bottom_north_east", + "from": [8, 0, 0], + "to": [16, 8, 8], + "faces": { + "north": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "south": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#top"} + } + }, + { + "name": "top_north_east", + "from": [8, 8, 0], + "to": [16, 16, 8], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "south": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#top"} + } + }, + { + "name": "bottom_south_west", + "from": [0, 0, 8], + "to": [8, 8, 16], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "east": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "west": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#top"} + } + }, + { + "name": "top_south_west", + "from": [0, 8, 8], + "to": [8, 16, 16], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "east": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#top"} + } + }, + { + "name": "bottom_north_west", + "from": [0, 0, 0], + "to": [8, 8, 8], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#top"} + } + }, + { + "name": "top_north_west", + "from": [0, 8, 0], + "to": [8, 16, 8], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/assets/models/ignimious_bulb.bbmodel b/assets/models/ignimious_bulb.bbmodel new file mode 100644 index 00000000..2c3b6593 --- /dev/null +++ b/assets/models/ignimious_bulb.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.5","model_format":"modded_entity","box_uv":true},"name":"ignimious_bulb","model_identifier":"","modded_entity_version":"Fabric 1.17+","modded_entity_flip_y":true,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":256,"height":256},"elements":[{"name":"jaw","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-23.956753782540364,-5.009498774448022,-24.13052619222006],"to":[24.043246217459636,17.990501225551977,23.86947380777994],"autouv":0,"color":8,"origin":[0.04324621745963608,-5.009498774448022,-0.13052619222006],"faces":{"north":{"uv":[48,48,96,71],"texture":0},"east":{"uv":[0,48,48,71],"texture":0},"south":{"uv":[144,48,192,71],"texture":0},"west":{"uv":[96,48,144,71],"texture":0},"up":{"uv":[96,48,48,0],"texture":0},"down":{"uv":[144,0,96,48],"texture":0}},"type":"cube","uuid":"800563f2-e637-d42d-6c38-616b6b34c9ca"},{"name":"head","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-22.956753782540364,10.990501225551977,-22.13052619222006],"to":[23.043246217459636,33.99050122555197,23.86947380777994],"autouv":0,"color":8,"rotation":[7.5,0,0],"origin":[0.04324621745963608,17.990501225551977,23.86947380777994],"uv_offset":[0,71],"faces":{"north":{"uv":[46,117,92,140],"texture":0},"east":{"uv":[0,117,46,140],"texture":0},"south":{"uv":[138,117,184,140],"texture":0},"west":{"uv":[92,117,138,140],"texture":0},"up":{"uv":[92,117,46,71],"texture":0},"down":{"uv":[138,71,92,117],"texture":0}},"type":"cube","uuid":"e6d05459-6b50-12b5-a46a-eb72b97f7c09"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-19,-9,-61],"to":[13,-9,-29],"autouv":0,"color":9,"rotation":[27.320650966819105,-50.33976442181192,-6.793186828559589],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"42f404db-2eb0-2efd-bb09-8810376fcfbc"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-16,-3,-60],"to":[16,-3,-28],"autouv":0,"color":9,"rotation":[16.681567243909033,33.56061965535128,3.4685333161320835],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"4c00dc9a-fef0-30b4-0f06-8bee9fe9b898"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-16,-6,-56],"to":[16,-6,-24],"autouv":0,"color":9,"rotation":[-162.2403464416216,58.02175378934316,-176.80418113816847],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"5db8a4ea-e690-f657-b64a-323ec0c3a305"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-24,-6,-62],"to":[8,-6,-30],"autouv":0,"color":9,"rotation":[-169.75684992659043,-35.13964450125722,-170.6241287065347],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"b2bbe86a-3379-0246-9181-127aed715432"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-16,-13,-58],"to":[16,-13,-26],"autouv":0,"color":9,"rotation":[144.36680378552091,-83.99459398899594,-115.75078983303227],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"c63affed-9e2a-84af-5766-631a13679757"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-16,-9,-59],"to":[16,-9,-27],"autouv":0,"color":9,"rotation":[28.459049469615778,-5.7440354970636935,0.03610759351412133],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"89bc35ae-6e61-dfa8-745f-0e317bb63260"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-12,-4,-52],"to":[20,-4,-20],"autouv":0,"color":9,"rotation":[40.560964930961916,83.77970397136126,27.74070945144022],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"0a5c37c6-ad13-697a-58cd-934841edb237"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-18,-15,-61],"to":[14,-15,-29],"autouv":0,"color":9,"rotation":[-149.33066739822777,4.299459188610997,-178.742402181358],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"d8b4bbe0-9707-0652-35e6-e28a54179fa3"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-34,-10,-84],"to":[30,-10,-20],"autouv":0,"color":9,"rotation":[111.7261647239295,-85.5550612830785,-96.58324240444844],"origin":[0,-3,-1],"uv_offset":[-64,140],"faces":{"north":{"uv":[0,204,64,204],"texture":0},"east":{"uv":[-64,204,0,204],"texture":0},"south":{"uv":[128,204,192,204],"texture":0},"west":{"uv":[64,204,128,204],"texture":0},"up":{"uv":[64,204,0,140],"texture":0},"down":{"uv":[128,140,64,204],"texture":0}},"type":"cube","uuid":"4e7c3e97-535b-9261-83a4-80fb76cc13d4"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-34,-10,-84],"to":[30,-10,-20],"autouv":0,"color":9,"rotation":[62.10613287479732,85.7200584727182,46.23842221938251],"origin":[0,-3,-1],"uv_offset":[-64,140],"faces":{"north":{"uv":[0,204,64,204],"texture":0},"east":{"uv":[-64,204,0,204],"texture":0},"south":{"uv":[128,204,192,204],"texture":0},"west":{"uv":[64,204,128,204],"texture":0},"up":{"uv":[64,204,0,140],"texture":0},"down":{"uv":[128,140,64,204],"texture":0}},"type":"cube","uuid":"2184d35d-e78b-c493-b1c4-a817cb45fa8b"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-34,-10,-84],"to":[30,-10,-20],"autouv":0,"color":9,"rotation":[-164.484342402648,7.951483564968085,-175.61972440808503],"origin":[0,-3,-1],"uv_offset":[-64,140],"faces":{"north":{"uv":[0,204,64,204],"texture":0},"east":{"uv":[-64,204,0,204],"texture":0},"south":{"uv":[128,204,192,204],"texture":0},"west":{"uv":[64,204,128,204],"texture":0},"up":{"uv":[64,204,0,140],"texture":0},"down":{"uv":[128,140,64,204],"texture":0}},"type":"cube","uuid":"24212edf-8ab0-e1e5-b67e-dd27ca7fee4f"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-34,-6,-84],"to":[30,-6,-20],"autouv":0,"color":9,"rotation":[15.186954129789529,2.0049709481247944,-1.9011648291348386],"origin":[0,-3,-1],"uv_offset":[-64,140],"faces":{"north":{"uv":[0,204,64,204],"texture":0},"east":{"uv":[-64,204,0,204],"texture":0},"south":{"uv":[128,204,192,204],"texture":0},"west":{"uv":[64,204,128,204],"texture":0},"up":{"uv":[64,204,0,140],"texture":0},"down":{"uv":[128,140,64,204],"texture":0}},"type":"cube","uuid":"dfd15693-f566-9bc7-b953-d303dec3ba18"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-17.373117897112767,65.61517788582364,17.389978221486494],"to":[14.626882102887233,65.61517788582364,49.389978221486494],"autouv":0,"color":9,"rotation":[113.4590494696158,-5.744035497063647,0.03610759351412453],"origin":[-1.3731178971127687,38.61517788582365,12.389978221486501],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"e020ca0b-24eb-a3cc-43ed-e87b0013e04d"}],"outliner":[{"name":"head","origin":[0,17,24],"rotation":[7.492942810808386,-0.32621370972236063,2.478638942680466],"color":0,"uuid":"e54419b9-7a64-8b11-6ad9-cea74cb14f41","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["e6d05459-6b50-12b5-a46a-eb72b97f7c09","800563f2-e637-d42d-6c38-616b6b34c9ca","e020ca0b-24eb-a3cc-43ed-e87b0013e04d"]},{"name":"leaves","origin":[0,-3,-1],"color":0,"uuid":"7cf17cab-353d-fddc-814d-3b708cb33637","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["4c00dc9a-fef0-30b4-0f06-8bee9fe9b898","42f404db-2eb0-2efd-bb09-8810376fcfbc","b2bbe86a-3379-0246-9181-127aed715432","c63affed-9e2a-84af-5766-631a13679757","4e7c3e97-535b-9261-83a4-80fb76cc13d4","dfd15693-f566-9bc7-b953-d303dec3ba18","2184d35d-e78b-c493-b1c4-a817cb45fa8b","24212edf-8ab0-e1e5-b67e-dd27ca7fee4f","89bc35ae-6e61-dfa8-745f-0e317bb63260","0a5c37c6-ad13-697a-58cd-934841edb237","d8b4bbe0-9707-0652-35e6-e28a54179fa3","5db8a4ea-e690-f657-b64a-323ec0c3a305"]}],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/assets/models/bulb.png","name":"bulb.png","folder":"block","namespace":"","id":"0","particle":false,"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":"742fdfc6-ad6a-1d7e-e84d-c8133537cff8","relative_path":"../bulb.png","source":"data:image/png;base64,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"}],"fabricOptions":{"header":"package com.example.mod;","entity":"Entity","render":"","members":""}} \ No newline at end of file diff --git a/assets/models/slime_pustule_cap.json b/assets/models/slime_pustule_cap.json deleted file mode 100644 index f337aa4f..00000000 --- a/assets/models/slime_pustule_cap.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "parent": "minecraft:block/cube_all", - "textures": { - "all": "unicopia:block/slime_pustule", - "particle": "unicopia:block/slime_pustule" - }, - "elements": [ - { - "from": [7, 14.9, 7], - "to": [12, 16, 12], - "faces": { - "north": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "east": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "south": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "west": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "from": [3, 15, 4], - "to": [9, 16, 10], - "faces": { - "north": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "east": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "south": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "west": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "rope", - "from": [7, 10, 7], - "to": [9, 15, 9], - "faces": { - "north": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "east": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "south": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "west": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "up": {"uv": [2, 2, 4, 4], "texture": "#all"}, - "down": {"uv": [2, 2, 4, 4], "texture": "#all"} - } - } - ] -} \ No newline at end of file diff --git a/assets/models/slime_pustule_pod.json b/assets/models/slime_pustule_pod.json deleted file mode 100644 index 7c15dd7b..00000000 --- a/assets/models/slime_pustule_pod.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "parent": "minecraft:block/cube_all", - "textures": { - "all": "unicopia:block/slime_pustule", - "particle": "unicopia:block/slime_pustule" - }, - "elements": [ - { - "name": "rope", - "from": [7.5, 0, 7.5], - "to": [8.5, 16, 8.5], - "faces": { - "north": {"uv": [12, 0, 13, 16], "texture": "#all"}, - "east": {"uv": [13, 0, 14, 16], "texture": "#all"}, - "south": {"uv": [15, 0, 16, 16], "texture": "#all"}, - "west": {"uv": [14, 0, 15, 16], "texture": "#all"}, - "up": {"uv": [13, 0, 14, 1], "texture": "#all"}, - "down": {"uv": [12, 0, 13, 1], "texture": "#all"} - } - }, - { - "from": [7, 14.9, 7], - "to": [12, 16, 12], - "faces": { - "north": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "east": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "south": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "west": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "from": [3, 15, 4], - "to": [9, 16, 10], - "faces": { - "north": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "east": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "south": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "west": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "rope", - "from": [7, 10, 7], - "to": [9, 15, 9], - "faces": { - "north": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "east": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "south": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "west": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "up": {"uv": [2, 2, 4, 4], "texture": "#all"}, - "down": {"uv": [2, 2, 4, 4], "texture": "#all"} - } - }, - { - "name": "rope", - "from": [7, 15, 7], - "to": [9, 20, 9], - "faces": { - "north": {"uv": [2, 7, 4, 13], "texture": "#all"}, - "east": {"uv": [2, 7, 4, 13], "texture": "#all"}, - "south": {"uv": [2, 7, 4, 13], "texture": "#all"}, - "west": {"uv": [2, 7, 4, 13], "texture": "#all"}, - "up": {"uv": [2, 2, 4, 4], "texture": "#all"}, - "down": {"uv": [2, 2, 4, 4], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [5, 10, 5], - "to": [11, 13, 11], - "faces": { - "north": {"uv": [0, 6, 6, 9], "texture": "#all"}, - "east": {"uv": [0, 6, 6, 9], "texture": "#all"}, - "south": {"uv": [0, 6, 6, 9], "texture": "#all"}, - "west": {"uv": [0, 6, 6, 9], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [6, 13, 6], - "to": [10, 15, 10], - "faces": { - "north": {"uv": [0, 6, 6, 8], "texture": "#all"}, - "east": {"uv": [0, 6, 6, 8], "texture": "#all"}, - "south": {"uv": [0, 6, 6, 8], "texture": "#all"}, - "west": {"uv": [0, 6, 6, 8], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [5, 0, 5], - "to": [11, 1, 11], - "faces": { - "north": {"uv": [0, 13, 6, 14], "texture": "#all"}, - "east": {"uv": [0, 13, 6, 14], "texture": "#all"}, - "south": {"uv": [0, 13, 6, 14], "texture": "#all"}, - "west": {"uv": [0, 13, 6, 14], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [4, 1, 4], - "to": [12, 10, 12], - "faces": { - "north": {"uv": [0, 6, 6, 14], "texture": "#all"}, - "east": {"uv": [0, 6, 6, 14], "texture": "#all"}, - "south": {"uv": [0, 6, 6, 14], "texture": "#all"}, - "west": {"uv": [0, 6, 6, 14], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [5, 2, 5], - "to": [11, 9, 11], - "faces": { - "north": {"uv": [7, 7, 11, 13], "texture": "#all"}, - "east": {"uv": [7, 7, 11, 13], "texture": "#all"}, - "south": {"uv": [7, 7, 11, 13], "texture": "#all"}, - "west": {"uv": [7, 7, 11, 13], "texture": "#all"}, - "up": {"uv": [7, 1, 11, 5], "texture": "#all"}, - "down": {"uv": [7, 1, 11, 5], "texture": "#all"} - } - } - ] -} \ No newline at end of file diff --git a/assets/models/slime_pustule_rope.json b/assets/models/slime_pustule_rope.json deleted file mode 100644 index aab23dfc..00000000 --- a/assets/models/slime_pustule_rope.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parent": "minecraft:block/cube_all", - "textures": { - "all": "unicopia:block/slime_pustule", - "particle": "unicopia:block/slime_pustule" - }, - "elements": [ - { - "name": "rope", - "from": [7.5, 0, 7.5], - "to": [8.5, 16, 8.5], - "faces": { - "north": {"uv": [12, 0, 13, 16], "texture": "#all"}, - "east": {"uv": [13, 0, 14, 16], "texture": "#all"}, - "south": {"uv": [15, 0, 16, 16], "texture": "#all"}, - "west": {"uv": [14, 0, 15, 16], "texture": "#all"}, - "up": {"uv": [13, 0, 14, 1], "texture": "#all"}, - "down": {"uv": [12, 0, 13, 1], "texture": "#all"} - } - } - ] -} \ No newline at end of file diff --git a/assets/models/tentacle.bbmodel b/assets/models/tentacle.bbmodel new file mode 100644 index 00000000..0ccec62c --- /dev/null +++ b/assets/models/tentacle.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.5","model_format":"modded_entity","box_uv":true},"name":"tentacle","model_identifier":"","modded_entity_version":"Fabric 1.17+","modded_entity_flip_y":true,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":128,"height":128},"elements":[{"name":"a","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-7,-6,-7],"to":[7,10,7],"autouv":0,"color":7,"origin":[0,-6,0],"faces":{"north":{"uv":[14,14,28,30],"texture":0},"east":{"uv":[0,14,14,30],"texture":0},"south":{"uv":[42,14,56,30],"texture":0},"west":{"uv":[28,14,42,30],"texture":0},"up":{"uv":[28,14,14,0],"texture":0},"down":{"uv":[42,0,28,14],"texture":0}},"type":"cube","uuid":"43deca74-ef0e-9496-f060-a569a5f66a3c"},{"name":"b","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,8,-6],"to":[6,27,6],"autouv":0,"color":7,"origin":[0,0,0],"uv_offset":[0,30],"faces":{"north":{"uv":[12,42,24,61],"texture":0},"east":{"uv":[0,42,12,61],"texture":0},"south":{"uv":[36,42,48,61],"texture":0},"west":{"uv":[24,42,36,61],"texture":0},"up":{"uv":[24,42,12,30],"texture":0},"down":{"uv":[36,30,24,42],"texture":0}},"type":"cube","uuid":"2f7bd122-e685-5397-518a-d566774b5f7f"},{"name":"c","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,25,-5],"to":[5,48,5],"autouv":0,"color":7,"origin":[0,26,0],"uv_offset":[48,20],"faces":{"north":{"uv":[58,30,68,53],"texture":0},"east":{"uv":[48,30,58,53],"texture":0},"south":{"uv":[78,30,88,53],"texture":0},"west":{"uv":[68,30,78,53],"texture":0},"up":{"uv":[68,30,58,20],"texture":0},"down":{"uv":[78,20,68,30],"texture":0}},"type":"cube","uuid":"a32e4c53-99d7-93d5-23f7-ae163af4dae5"},{"name":"d","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-4,45,-4],"to":[4,66,4],"autouv":0,"color":7,"origin":[0,26,0],"uv_offset":[40,53],"faces":{"north":{"uv":[48,61,56,82],"texture":0},"east":{"uv":[40,61,48,82],"texture":0},"south":{"uv":[64,61,72,82],"texture":0},"west":{"uv":[56,61,64,82],"texture":0},"up":{"uv":[56,61,48,53],"texture":0},"down":{"uv":[64,53,56,61],"texture":0}},"type":"cube","uuid":"a4c5b57f-c555-154e-e673-7e114f6e5a21"},{"name":"e","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-3,64,-3],"to":[3,86,3],"autouv":0,"color":7,"origin":[-1.1793215979571126e-16,26,-4.382008627821946e-33],"uv_offset":[0,61],"faces":{"north":{"uv":[6,67,12,89],"texture":0},"east":{"uv":[0,67,6,89],"texture":0},"south":{"uv":[18,67,24,89],"texture":0},"west":{"uv":[12,67,18,89],"texture":0},"up":{"uv":[12,67,6,61],"texture":0},"down":{"uv":[18,61,12,67],"texture":0}},"type":"cube","uuid":"c8af392a-28ea-8a9f-cc0f-e8f19558f993"},{"name":"f","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-3,85,-3],"to":[3,100,3],"autouv":0,"color":7,"origin":[-2.620714662126916e-16,47,-9.737796950715435e-33],"uv_offset":[72,53],"faces":{"north":{"uv":[78,59,84,74],"texture":0},"east":{"uv":[72,59,78,74],"texture":0},"south":{"uv":[90,59,96,74],"texture":0},"west":{"uv":[84,59,90,74],"texture":0},"up":{"uv":[84,59,78,53],"texture":0},"down":{"uv":[90,53,84,59],"texture":0}},"type":"cube","uuid":"1d4836bc-6b01-9b8b-2958-1280f80f91b7"},{"name":"g","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-2.5999999999999996,99,-2.5999999999999996],"to":[2.5999999999999996,114,2.599999999999998],"autouv":0,"color":7,"origin":[-3.6034826604245074e-16,0,1],"uv_offset":[56,0],"faces":{"north":{"uv":[61.199999999999996,5.1999999999999975,66.39999999999999,20.199999999999996],"texture":0},"east":{"uv":[56,5.1999999999999975,61.199999999999996,20.199999999999996],"texture":0},"south":{"uv":[71.6,5.1999999999999975,76.8,20.199999999999996],"texture":0},"west":{"uv":[66.39999999999999,5.1999999999999975,71.6,20.199999999999996],"texture":0},"up":{"uv":[66.39999999999999,5.1999999999999975,61.199999999999996,0],"texture":0},"down":{"uv":[71.6,0,66.39999999999999,5.1999999999999975],"texture":0}},"type":"cube","uuid":"4001f6b1-caf3-d76f-2d5b-8636c85cd0fb"},{"name":"h","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-2.0999999999999996,113,-2.0999999999999996],"to":[2.0999999999999996,128,2.099999999999998],"autouv":0,"color":7,"origin":[-4.455214925615753e-16,14,1],"uv_offset":[24,61],"faces":{"north":{"uv":[28.199999999999996,65.2,32.4,80.19999999999999],"texture":0},"east":{"uv":[24,65.2,28.199999999999996,80.19999999999999],"texture":0},"south":{"uv":[36.599999999999994,65.2,40.8,80.19999999999999],"texture":0},"west":{"uv":[32.4,65.2,36.599999999999994,80.19999999999999],"texture":0},"up":{"uv":[32.4,65.2,28.199999999999996,61],"texture":0},"down":{"uv":[36.599999999999994,61,32.4,65.2],"texture":0}},"type":"cube","uuid":"4ba83545-3fcf-726d-033c-a1ac411a4ae3"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[3,-0.6703481918674496,-7.772537561589672],"to":[17,-0.6703481918674496,6.227462438410338],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[3,-0.6703481918674496,-7.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"43754e70-3fcd-b60a-62ee-01eadc22607a"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[3,-0.6703481918674496,-7.772537561589672],"to":[17,-0.6703481918674496,6.227462438410338],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[3,-0.6703481918674496,-7.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100,14.000000000000002,114,14.000000000000002],"texture":0},"east":{"uv":[86,14.000000000000002,100,14.000000000000002],"texture":0},"south":{"uv":[128,14.000000000000002,142,14.000000000000002],"texture":0},"west":{"uv":[114,14.000000000000002,128,14.000000000000002],"texture":0},"up":{"uv":[114,14.000000000000002,100,0],"texture":0},"down":{"uv":[128,0,114,14.000000000000002],"texture":0}},"type":"cube","uuid":"e5054bff-a980-a8dc-0221-58208085978f"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[5,-0.6703481918674511,4.227462438410328],"to":[19,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[5,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"13ebb478-c830-3a6e-858d-2269a5ea0642"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[5,-0.6703481918674511,4.227462438410328],"to":[19,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[5,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"2a87966b-98f6-fe32-0698-06d07dd0caa9"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674511,4.227462438410328],"to":[8,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-6,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"b2e2dbad-e1e3-ecb6-41d7-21cb1908deb8"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674511,4.227462438410328],"to":[8,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-6,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"f93aa31f-c197-3e66-58ad-a1c638ecce97"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674499,-6.772537561589672],"to":[8,-0.6703481918674499,7.227462438410338],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-6,-0.6703481918674499,-6.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"c1ca47a6-f9ba-555a-cc32-58d55b8809b9"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674499,-6.772537561589672],"to":[8,-0.6703481918674499,7.227462438410338],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-6,-0.6703481918674499,-6.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"b8b98b2d-632b-3acd-29a8-55efa4850547"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[3,-0.6703481918674496,-7.772537561589672],"to":[17,-0.6703481918674496,6.227462438410338],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[3,-0.6703481918674496,-7.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"e3aaee1e-958c-370c-a10d-94e142df629c"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[3,-0.6703481918674496,-7.772537561589672],"to":[17,-0.6703481918674496,6.227462438410338],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[3,-0.6703481918674496,-7.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"6bf59ab2-03d6-2995-17bc-76e27e52bf6c"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[5,-0.6703481918674511,4.227462438410328],"to":[19,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[5,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"666a373c-9dd2-0cfc-c1b7-13a5fe7fe392"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[5,-0.6703481918674511,4.227462438410328],"to":[19,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[5,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"ebaca892-208f-1ec8-a7b0-4b8bf692d869"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674511,4.227462438410328],"to":[8,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-6,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"02bb3148-7ad4-1fbe-f233-fcd02c1f00f4"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674511,4.227462438410328],"to":[8,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-6,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"ea8790f9-39c1-9607-7faf-4054f0782315"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674499,-6.772537561589672],"to":[8,-0.6703481918674499,7.227462438410338],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-6,-0.6703481918674499,-6.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"42f35de8-e9da-314a-ea8b-4ec3162ccc7b"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674499,-6.772537561589672],"to":[8,-0.6703481918674499,7.227462438410338],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-6,-0.6703481918674499,-6.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"2a148807-d884-e05b-680e-36ba4ce450e5"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-4,40.62422566484149,-3.3435187525409757],"to":[10,40.62422566484149,10.656481247459041],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-4,40.62422566484149,-3.3435187525409757],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"e0119d00-cad6-3842-bbe5-6885628ff35e"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-4,40.62422566484149,-3.3435187525409757],"to":[10,40.62422566484149,10.656481247459041],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-4,40.62422566484149,-3.3435187525409757],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"abe6d75d-fccf-7b97-e8ca-156f68f30b76"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2,60.13549813476108,3.0055163398616545],"to":[16,60.13549813476108,17.005516339861675],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[2,60.13549813476108,3.0055163398616545],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.000000000000018,114.00000000000001,14.000000000000018],"texture":0},"east":{"uv":[86,14.000000000000018,100.00000000000001,14.000000000000018],"texture":0},"south":{"uv":[128.00000000000003,14.000000000000018,142.00000000000003,14.000000000000018],"texture":0},"west":{"uv":[114.00000000000001,14.000000000000018,128.00000000000003,14.000000000000018],"texture":0},"up":{"uv":[114.00000000000001,14.000000000000018,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.000000000000018],"texture":0}},"type":"cube","uuid":"bf2e26ba-937a-73c8-3ab2-9240c2690b0b"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2,60.13549813476108,3.0055163398616545],"to":[16,60.13549813476108,17.005516339861675],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[2,60.13549813476108,3.0055163398616545],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.000000000000018,114.00000000000001,14.000000000000018],"texture":0},"east":{"uv":[86,14.000000000000018,100.00000000000001,14.000000000000018],"texture":0},"south":{"uv":[128.00000000000003,14.000000000000018,142.00000000000003,14.000000000000018],"texture":0},"west":{"uv":[114.00000000000001,14.000000000000018,128.00000000000003,14.000000000000018],"texture":0},"up":{"uv":[114.00000000000001,14.000000000000018,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.000000000000018],"texture":0}},"type":"cube","uuid":"61246d8c-3a21-f2e2-2b5e-656b618b9315"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2.0000000000000004,89.02514437942168,0.47799980017960014],"to":[16,89.02514437942168,14.47799980017963],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[2.0000000000000004,89.02514437942168,0.47799980017960014],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000003,14.000000000000021,114.00000000000003,14.000000000000021],"texture":0},"east":{"uv":[86,14.000000000000021,100.00000000000003,14.000000000000021],"texture":0},"south":{"uv":[128.00000000000006,14.000000000000021,142.00000000000006,14.000000000000021],"texture":0},"west":{"uv":[114.00000000000003,14.000000000000021,128.00000000000006,14.000000000000021],"texture":0},"up":{"uv":[114.00000000000003,14.000000000000021,100.00000000000003,0],"texture":0},"down":{"uv":[128.00000000000003,0,114.00000000000003,14.000000000000021],"texture":0}},"type":"cube","uuid":"ff5dbdd3-d6eb-27ba-e1ab-c1a0f550fd3e"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2.0000000000000004,89.02514437942168,0.47799980017960014],"to":[16,89.02514437942168,14.47799980017963],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[2.0000000000000004,89.02514437942168,0.47799980017960014],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000003,14.000000000000021,114.00000000000003,14.000000000000021],"texture":0},"east":{"uv":[86,14.000000000000021,100.00000000000003,14.000000000000021],"texture":0},"south":{"uv":[128.00000000000006,14.000000000000021,142.00000000000006,14.000000000000021],"texture":0},"west":{"uv":[114.00000000000003,14.000000000000021,128.00000000000006,14.000000000000021],"texture":0},"up":{"uv":[114.00000000000003,14.000000000000021,100.00000000000003,0],"texture":0},"down":{"uv":[128.00000000000003,0,114.00000000000003,14.000000000000021],"texture":0}},"type":"cube","uuid":"8e0f248f-1228-4a38-2a74-cb0d23718fdc"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-22.881490097225765,145.88837332325411,6.8760974475566785],"to":[-8.881490097225756,145.88837332325411,20.876097447556763],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000009,14.000000000000085,114.0000000000001,14.000000000000085],"texture":0},"east":{"uv":[86,14.000000000000085,100.00000000000009,14.000000000000085],"texture":0},"south":{"uv":[128.00000000000017,14.000000000000085,142.0000000000002,14.000000000000085],"texture":0},"west":{"uv":[114.0000000000001,14.000000000000085,128.00000000000017,14.000000000000085],"texture":0},"up":{"uv":[114.0000000000001,14.000000000000085,100.00000000000009,0],"texture":0},"down":{"uv":[128.0000000000001,0,114.0000000000001,14.000000000000085],"texture":0}},"type":"cube","uuid":"817666d5-286f-6161-8446-3970704a3265"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0.16641666179338577,107.95005796968688,-16.171809311462464],"to":[14.166416661793397,107.95005796968688,-2.17180931146247],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100,13.999999999999993,114,13.999999999999993],"texture":0},"east":{"uv":[86,13.999999999999993,100,13.999999999999993],"texture":0},"south":{"uv":[128,13.999999999999993,142,13.999999999999993],"texture":0},"west":{"uv":[114,13.999999999999993,128,13.999999999999993],"texture":0},"up":{"uv":[114,13.999999999999993,100,0],"texture":0},"down":{"uv":[128,0,114,13.999999999999993],"texture":0}},"type":"cube","uuid":"5f3e16dd-eed6-733e-e1db-f1934b558e88"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-22.881490097225765,145.88837332325411,6.8760974475566785],"to":[-8.881490097225756,145.88837332325411,20.876097447556763],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000009,14.000000000000085,114.00000000000009,14.000000000000085],"texture":0},"east":{"uv":[86,14.000000000000085,100.00000000000009,14.000000000000085],"texture":0},"south":{"uv":[128.00000000000017,14.000000000000085,142.00000000000017,14.000000000000085],"texture":0},"west":{"uv":[114.00000000000009,14.000000000000085,128.00000000000017,14.000000000000085],"texture":0},"up":{"uv":[114.00000000000009,14.000000000000085,100.00000000000009,0],"texture":0},"down":{"uv":[128.0000000000001,0,114.00000000000009,14.000000000000085],"texture":0}},"type":"cube","uuid":"573cf719-c66a-d73e-148d-d14e9617d0ac"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0.16641666179338577,107.95005796968688,-16.171809311462464],"to":[14.166416661793397,107.95005796968688,-2.17180931146247],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100,13.999999999999995,114,13.999999999999995],"texture":0},"east":{"uv":[86,13.999999999999995,100,13.999999999999995],"texture":0},"south":{"uv":[128,13.999999999999995,142,13.999999999999995],"texture":0},"west":{"uv":[114,13.999999999999995,128,13.999999999999995],"texture":0},"up":{"uv":[114,13.999999999999995,100,0],"texture":0},"down":{"uv":[128,0,114,13.999999999999995],"texture":0}},"type":"cube","uuid":"e4e3e2ae-0684-ee78-4ec9-33f0f9461b19"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-22.881490097225765,145.88837332325411,6.8760974475566785],"to":[-8.881490097225756,145.88837332325411,20.876097447556763],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000009,14.000000000000085,114.00000000000009,14.000000000000085],"texture":0},"east":{"uv":[86,14.000000000000085,100.00000000000009,14.000000000000085],"texture":0},"south":{"uv":[128.00000000000017,14.000000000000085,142.00000000000017,14.000000000000085],"texture":0},"west":{"uv":[114.00000000000009,14.000000000000085,128.00000000000017,14.000000000000085],"texture":0},"up":{"uv":[114.00000000000009,14.000000000000085,100.00000000000009,0],"texture":0},"down":{"uv":[128.0000000000001,0,114.00000000000009,14.000000000000085],"texture":0}},"type":"cube","uuid":"c59ea2f3-dbd7-5b92-4640-4acc2121a8b2"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0.16641666179338577,107.95005796968688,-16.171809311462464],"to":[14.166416661793397,107.95005796968688,-2.17180931146247],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100,13.999999999999995,114,13.999999999999995],"texture":0},"east":{"uv":[86,13.999999999999995,100,13.999999999999995],"texture":0},"south":{"uv":[128,13.999999999999995,142,13.999999999999995],"texture":0},"west":{"uv":[114,13.999999999999995,128,13.999999999999995],"texture":0},"up":{"uv":[114,13.999999999999995,100,0],"texture":0},"down":{"uv":[128,0,114,13.999999999999995],"texture":0}},"type":"cube","uuid":"3bb469e2-e5a5-7cfc-3e9c-6fd43b8a3ca0"}],"outliner":[{"name":"bone_a","origin":[0,0,0],"rotation":[-7.951386703658792e-15,0,0],"color":0,"uuid":"836e9354-0309-70b5-cb60-e7f38118e743","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["43deca74-ef0e-9496-f060-a569a5f66a3c",{"name":"flower_4","origin":[-6,-0.6703481918674499,-6.772537561589672],"rotation":[-91.26598229300457,52.08446097361874,-64.05377573888674],"color":0,"uuid":"2ea8b2cf-a2c0-c454-2fb5-d9bc522a921c","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["c1ca47a6-f9ba-555a-cc32-58d55b8809b9","b8b98b2d-632b-3acd-29a8-55efa4850547",{"name":"flower_8","origin":[-6,-0.6703481918674499,-6.772537561589672],"rotation":[-167.64364206893785,46.98788282368353,-125.90165504433261],"color":0,"uuid":"02ef3343-dfcc-9af4-29a2-6dc427c70898","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["42f35de8-e9da-314a-ea8b-4ec3162ccc7b","2a148807-d884-e05b-680e-36ba4ce450e5"]}]},{"name":"flower_3","origin":[-6,-0.6703481918674511,4.227462438410328],"rotation":[137.96042965468234,-13.430580338215425,-145.38236406964168],"color":0,"uuid":"2fc2f20b-05ce-0410-dd40-8d55ec1d814a","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["b2e2dbad-e1e3-ecb6-41d7-21cb1908deb8","f93aa31f-c197-3e66-58ad-a1c638ecce97",{"name":"flower_7","origin":[-6,-0.6703481918674511,4.227462438410328],"rotation":[137.96042965468234,-13.430580338215425,-145.38236406964168],"color":0,"uuid":"3c08633a-6d30-3951-f49a-b815826de0cd","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["02bb3148-7ad4-1fbe-f233-fcd02c1f00f4","ea8790f9-39c1-9607-7faf-4054f0782315"]}]},{"name":"flower_2","origin":[5,-0.6703481918674511,4.227462438410328],"rotation":[72.75299322235041,-55.44873947197246,-103.02095072899253],"color":0,"uuid":"8cc35e91-dbf2-1e20-efc6-d0c6dc9e1e32","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["13ebb478-c830-3a6e-858d-2269a5ea0642","2a87966b-98f6-fe32-0698-06d07dd0caa9",{"name":"flower_6","origin":[5,-0.6703481918674511,4.227462438410328],"rotation":[72.75299322235041,-55.44873947197246,-103.02095072899253],"color":0,"uuid":"93297f86-e3ca-4d71-a6c8-298ff77bbb1a","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["666a373c-9dd2-0cfc-c1b7-13a5fe7fe392","ebaca892-208f-1ec8-a7b0-4b8bf692d869"]}]},{"name":"flower_1","origin":[3,-0.6703481918674496,-7.772537561589672],"rotation":[-34.96649542120608,3.067430398401471,-33.59739024796235],"color":0,"uuid":"59363886-4b5a-2a3c-75cd-8d6276de69b3","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["e5054bff-a980-a8dc-0221-58208085978f","43754e70-3fcd-b60a-62ee-01eadc22607a",{"name":"flower_5","origin":[3,-0.6703481918674496,-7.772537561589672],"rotation":[-34.96649542120608,3.067430398401471,-33.59739024796235],"color":0,"uuid":"0265d420-aeaa-b538-1252-89df12b0f9b6","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["e3aaee1e-958c-370c-a10d-94e142df629c","6bf59ab2-03d6-2995-17bc-76e27e52bf6c"]}]},{"name":"bone_b","origin":[0,9,0],"rotation":[15,0,0],"color":0,"uuid":"aa6d7f64-89b9-a900-a3dc-3a9fcdc22618","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["2f7bd122-e685-5397-518a-d566774b5f7f",{"name":"bone_c","origin":[0,25,0],"rotation":[-6.599650964036798e-14,0,0],"color":0,"uuid":"7b4502a2-fe6a-53ad-2f9f-391f31027828","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":[{"name":"flower_9","origin":[-4,40.62422566484149,-3.3435187525409757],"rotation":[-167.64364206893785,46.987882823683556,-140.9016550443326],"color":0,"uuid":"5ba196d8-95ae-bace-28cb-48bdb2d6cbac","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["e0119d00-cad6-3842-bbe5-6885628ff35e","abe6d75d-fccf-7b97-e8ca-156f68f30b76"]},"a32e4c53-99d7-93d5-23f7-ae163af4dae5",{"name":"bone_d","origin":[0,43,0],"rotation":[10.000000000000005,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"286e227f-8a7a-cfd3-27a0-86aed0c10bb7","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":[{"name":"flower_10","origin":[2,60.13549813476108,3.0055163398616545],"rotation":[163.90070263038493,34.045350837165316,140.2721846400254],"color":0,"uuid":"1ed2c36a-c359-91ea-40ba-572a6c8757f7","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["bf2e26ba-937a-73c8-3ab2-9240c2690b0b","61246d8c-3a21-f2e2-2b5e-656b618b9315"]},"a4c5b57f-c555-154e-e673-7e114f6e5a21",{"name":"bone_e","origin":[0,61,0],"rotation":[-10,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"960ce6d7-f146-7814-e0c9-b11b5b43956a","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["c8af392a-28ea-8a9f-cc0f-e8f19558f993",{"name":"bone_f","origin":[0,83,0],"rotation":[-10,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"3f876c03-f9b2-1dac-08bc-990abce00d76","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["1d4836bc-6b01-9b8b-2958-1280f80f91b7",{"name":"flower_11","origin":[2.0000000000000004,89.02514437942168,0.47799980017960014],"rotation":[-158.0615625451014,-19.935030254861065,145.712572187446],"color":0,"uuid":"42c04c52-8414-cc27-f1a2-bce1e6fdc48c","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["ff5dbdd3-d6eb-27ba-e1ab-c1a0f550fd3e","8e0f248f-1228-4a38-2a74-cb0d23718fdc"]},{"name":"bone_g","origin":[0,98,0],"rotation":[-17.5,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"87fdafb8-862b-0fc4-f194-483e4c6ab1d2","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["4001f6b1-caf3-d76f-2d5b-8636c85cd0fb",{"name":"bone_h","origin":[0,111,0],"rotation":[-15,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"bd8fb7b0-ef45-7564-a12a-05cb47421493","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["4ba83545-3fcf-726d-033c-a1ac411a4ae3",{"name":"flower_12","origin":[-4.852359674770397e-16,126.1462282992986,0.6365198729059891],"rotation":[-158.06156254510137,-19.93503025486101,170.7125721874457],"color":0,"uuid":"faa91113-8626-24ec-baa2-9003ea1e0eda","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["817666d5-286f-6161-8446-3970704a3265","5f3e16dd-eed6-733e-e1db-f1934b558e88",{"name":"flower_13","origin":[-4.852359674770397e-16,126.1462282992986,0.6365198729059891],"rotation":[-158.06156254510134,-19.935030254861015,148.21257218744574],"color":0,"uuid":"749d951a-ecbc-4197-9c32-365bbb0a98a9","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["573cf719-c66a-d73e-148d-d14e9617d0ac","e4e3e2ae-0684-ee78-4ec9-33f0f9461b19"]},{"name":"flower_14","origin":[-4.852359674770397e-16,126.1462282992986,0.6365198729059891],"rotation":[111.22147558087971,59.83922506916349,99.72701164210366],"color":0,"uuid":"3c36c30a-bdb5-bede-7d1c-4073204f716c","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["c59ea2f3-dbd7-5b92-4640-4acc2121a8b2","3bb469e2-e5a5-7cfc-3e9c-6fd43b8a3ca0"]}]}]}]}]}]}]}]}]}]}],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/assets/models/tentacle.png","name":"tentacle.png","folder":"block","namespace":"","id":"0","particle":false,"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":"59729803-364e-77c5-ab9a-5c088a316d59","relative_path":"../tentacle.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAAIABJREFUeF7kvXnwbdlV3/fZw5nufH/jm6d+PUlqiRYSEhJCIBEMhoRRljCSMJRi4qqEVAY7KVeqUnY5cdnlsp14Aoc4tkmcxImBKhEghsgChBBCoqWWep7e/H7j/d3xjHtI7dNS/iAI9/u9fnQD3fXqveq+9/z2O2edvdda32EJjvnPIw9+xKciRZmGxEsirVlVBSKRYByN8PScoiPhhjE0WqGkYuyg8p4ZnrFpiGONUBpvSvoqZdYYjBDUtkTKGCNg5Q3Pvvi/imMu9f/3tfc+/CPekWJtQyIVtav5zWf/2at2/Vdrnb/3Ot/6nb/i/80v/Tuv6jqPfbF3XvpzXoqGnodKZRSuIRUC6wy7eE64mtxrBt0ON21O3wgKlfGAr1jYmFjUbRBUQtKLFYfLBaeiHsqL9qEvpaVuKkSScYqIjz/z08de6++9kR9+43/smyTioDbMfMkJPeYXvvjXX7Xrv9oB8F//tyv/V/5yV3zn933B4x2/9PNvfdXWeuwLvfXSh/047TBb7LCerKHCm608B75hTSXEHhoPsTNciSN0s+CEzzjylkJIzkhPIwULH5G6kr5PMMKhdczKOxpnyW1DamuE0Hzmyqu4AzzyMb/uI1ZNTZJ12EHw2S/898e+F6/2A//q9d7zbT/rw59/41e/X4Qg+OQn/w1Z5wTToyf5zK999FVZ77Ev8i0P/lm/XkQ8oysuqIiVcxw4Qd8r8gik8HSMxYXtXcWUtmZTxkyrGd1kjK+nHEUxA6HBOoxzbCrFgfdoLAdW8BYleck7VHXEb974hWOv9fc+oG975Mf9iWjEtcU+y3RI7Go+88Q/eNWufzcB8dW3PVzjqwEQ/qyjPqaZs3Xim9nf/U1+/Ve+91VZ77Ev8p77Pub7OqE0Cw5jxcBJSlPj6zkuzpBygBcFjfcoJ4mwTJKITuVZjyRTU6GjFFmU+LSLbkompuRkOmBJg6xLnEoZt/kC/PYLP3Pstf7eB/LGR37Mf1O0xrVqRaMiNDW//PhPvmrXv5sACN8NQRB+D9v++//0J3xdT9t8aDp5nMsP/RiTwy+xd/uTPPGF/+au13zsC3z7pY/4BYLalfRkQpMKuk5hnWViBVbAuvKsbEFHdliaitVoTDqZMZaClVRoV+NFgvAh8XNIKTmv++x7QW6n9IxmIg0GePyF4x0B73r0r/mT1nFfskFlC0zk+YK1fNvoBLVfcVhWTNMBlwTcmE+p5Ih3Riv+5dSx3Uk4UcOeP+CESFipMXlxyL964r889n17pcHx1SD47O88S1ketF8r89s0zYoLlz7A00/8Pb782F+563Uc+wLfcPEDvoiGXLQVCw8TDbNyyXayRkd6NmTC/mqHWimipM9CCjZdw5XakkqNwhJr0D6iEpChqbxBVA2FEJyIU3Kt6NU1T5sFL730fx5rre+//Bf9eidjFXfpWEvkKw50h4ek43ODHqfzgreqlFJ0eF73mK0OyKyjVBFja5hLwV6eczpNOZCOwjk+87n/9FhreSUPP2z73d45inyHbv88RwePkXXPUCyv88yTP8mZC99Nkm5RrG68tgHwngd+3K/sHKmiNuGTjaCXCCZC0bUgopirVc1F5alDmehrHIKjpmKjN6CuVyyaFTLewJolViSsScGObzgZKkkZI4jYr1eMpOIzz//zY930733LX/ejeI2d1QucdwlPixyifltd9OucNwjFQZbwkhjzNqe44XP6XvCFrMvloqTycEOCrxZ8XXaK7a7mr/7qh461llcSAF89+7POSbxzdPvnOJp8uX3gzz31j9uE+ML9H6IuJ3zLt3w/f//vvOWu1nLsL7/90kd9V8YkqqFqCvZVxkhIOkQcmZw0GWGqCSpSLKygWcvQ+yUbicY0OUJ0OfBTRiKhZy0i1OM+lIoRfSFwQrbXWUlLVAm+fO1/O9Zav/3Rv+ovVDkHDIj1nEHoS3jNyMFuKokrC2rMEEtNzFRqYl+ymYwYC3iss865yYvMK8uLieGtWvMzn/0vjrWWVxoA4XPf+N5/7lUoi1XGeO1NVNURs+mTPP34fwc4fujDP8k/+LuP3vU6jn2BN1z6qD8lLDtAx1tkrOk7ydy2ST2x9jhASU1ZLhgyxMSWkao5NJrGeRKhSLVm1tSYZsko6nFDGs6QsDsQ9I4a+tJzkPb5whP/6Fhr/aGH/6I/Eg605mZHcraAuol4MO5zs75Nnm6RFUeIrMPb0XxSeqxL2NQF41pztn+exXKXz7slKu2ys9jjC0/+jWOt5U4CIHz2697+N3x4MaJoyHD8Buaz51A6/UoQwOTgt+56Hce+wJsuftArFbMF7EQZSz/hZJMhtUALT+VSGmokKSeqI/ZCnS8bFr5m7FOcrsn0qN1iD23NiNAXkJw2BUciQoXOIQojPHuN5dmXjlcFvPXSf+DHccrUTBmuXaBcNjwcDVlGDa7aI/YpxpR4qTigz7puGCd9nrIWvdzhqHORh8wuX4rgvlKxL2J+68m7T75eaTC84z3/xNuQvNZL3vu+70FKxT/76Y+2X+8Sc/3g1479DMM1jv3l9z7w5/1Os+SCTkk8PF9O6UeaRPWwIiJ2FcuqoZNo5rYh1hGiyamFZi1eY88ecSpJeMEITuBInUXLLp4VU+PoyYi4CduyJI4Fv/jcPz3WWr/jDX/J41bE9DFpj4Oq5E3dDW6phmh5wBVX84h37HvPJZWwUx9QxCMQnsKBlEPGdkkpFVWTs/SC33n+7x1rLa/0of9+n/vx/+jTPrSuf/ofvrf92Vtr3+BdpIlczO39Tx57Pcf+4jsvf8QPjcfqLpmr8HHKuMp5AkPV9vW7lKJi00uOooRCx5w0guebQ+6Ptuk0c/aEhE6fQV0xsys2yZiGv1S1xEnFyudkeo1JnfPlq//Lsdb67Q//hNeyQ+o0t80+B2S8b3CWfV1QL+Ys4oyBKah9zLh3Ejl5guejIWtuThJ3kdF2mx98vt7hslMMheeffvlvHWstdxMAv993t9a+0Vs8ke6xs/erx1rTsb4UFvPw2e/3MurRVxlxfpvbusOYiEYpRiFxEwlNvWKox9SR5WZTkqkUSUkhUjp1zliWPO00J4m5pRT3W89ECtZsyUpLirpgKDvsqIQnXjgeFvCOCz/q97XkzQieFpquM8RCtGVo3yXsJoo31Tnz5CSFm9HYOVaOKM2KzXgdqxre0j/B40e3CUnZDZPz2FN/+9j37dUOgrMb7/bIjOvHDYC3nv+wHwYkTzj6yZBFPqEnIlIcS2Vo0ByVCy6pHrejDmdcwZPGMXChwaO4FQvOmZRuMmTfTIgtxNTU6Zi6OiJ3A4xrsGnGw16wqG7j03Vs0+CEZyNk+9biZEzPVOxLhZCSxEXEuqZ2HuMFXZWybpbsErFn5yTZOr6pCJnGhojYdQ6PwZuKsY7ZVRqc461RypelZC3usleXnBZDfBLh89vsfwVz2JYdusk6B/6A0gsGVlMqzyVnOVAZQnfJlzPwdXuUfeLZf/i6CYC7DSjx9ff9sF+FG6ljpNPISFL6mhWOM05RuoZhMuTIRjhxgKoEQsm2TJrUczoqwWMx3lC6kLY5FjridLXEi4yVqBmLiEMcXS85oOZUuoGzK6blgoFK2a0NOo0ZIUhkzG1jqKKIbjWlo7os6ymT9S73rcLVFY0vEUmPajHlIO4walZkOsFrQdZ4Fq6kaRryOON0Z8TJOOWLfsVD1ZDrsqabnGFj9QQ3ZYQWESdlj+uiC2YfAjbhCsZS47BEqsO+kpzBMAl5ju7wc0+8ftrGdx0Ajzz457xbLVkTXXTiMBYyq1iJAqM12nnmUlJ5ydiVaDmgG0UoP+NZqdgqa3rZOstyypqOuFkuiOIxF52njDVX6iXbURfV1Fzpp1yoYbcp6DmI6jnPSc83RGOeowEv2bCWbpyCcHREzPM2R9iSXCV0kay85W1Rj6PGctMcoVVCiqCyFUfA/ULxkm0wvuIhkVJnawgt6Edj5sKTC4GWArfYbY+XKRUmXaMWMcPqkNiGxrNHyQwbWY6sZqAHpPUuh1rRb0p+6ZnXP3fglQaGeP+5H/R5u+EXoDuIokDGESiP9xatu8xNxXqU4r3gilzxptKRi5RF0kFVU9J0i10zpbYRF5UiN2FHSZhZQ4RiSk0/1cSNI60cMlbclqGELLgYbfDlch8lPcKHTqIkkT1Scnas5yIWheZpu+IUiq7QyCbndtIhLmd0lGZJwiae6xTETUWjBcZr1gIi2T9H5QybpmSZ9XhADtm3OS9WB6Sy17agdboRDi2my0M6kcQkET0DRkVMXMPX+5ShEjzuKhArPvml4+Ujr/Sh/GF+Tpy/+EN+y1ikBCFiiuEJLlUrtHNcNSUIQ8epFkiJVY+VqDhBj6tmzlanS2E9ERLvRFu6vSRLzjrNxJU8FJKsEDiu5EZquORjbjUVmdCo0C7GtFjAJOlwsp4Tmr9XQtfQzunpiG2VcMNWLGOwNmGjKRFC0ZeOeW2QfomWfQbCcOQcC2dwtgal6AtHLBNM9xwzaiInORlQSNknq4841DBuBLum5uFsEy9g0swY+IhtXfGiEtwqHac8LZg1jhJqu+Kar/ntp/+HPz45wIWHPuj7tWe9XjLJNnmn7PIlV6K8JTUltdQkLkerjKXQeNtwYAzr8RodX5CLmBNSMrWWXtJB2prnTYmLkrbNO65WPC9yjBrybiX5rLW8EcEtpTHYFkZe2jnGScZRRBW4ASIjqo/opiMOqxlTJRnVJUZKOkJjvGNgcxYy4cAU9KSjthIrHIU39EVE5Rssmq8fnGfWSTgs5wjbcHnrIWaLCTKfYaIMWRRU45MM/JJ5PmGiIjpSo8OvuuSWzBjWh2xvvIGr85sUjeSZ518f3IFXY6cQD174fn+fGnE9YPF2SdU7x6rJsb5CNAVRyMLTATkNW7XlViS47ELWPqJKJJ2i4ooM/b6Xz/B+2MKVYF56XE8il1PQXaLOkNwI+lQ0TUVucmYjxSgfou2S8z7hRXGI9DFn4h5fLOZ0pWPNSxpfoEXKrhaMXcSOaejiMNLijWUooesT9qWlqnO6Ohw8Dm09l+ItDqKG3ME8HbAwK0Z5Trc3xIglg3SNMm9Y1wk3Fkd8/vrxMIdX42G8FtcQ73v4J/yoaWikY0bGI511buc3eKHJ2cz67Ncv3+DMK2axRTBi3S8pvaRjGuYC+s6jfU0jBC+4GivHnAmplGzoRpJJccD53kVuFXOuLm7S7/WIbIUKfQORkJZ7qGyjrf+/ZBwbUjIRjnXXcFp2uBKaS80K52oeSgbMnGVhNTJN2S8X9MwKEcdkwiPrwB/wHFiPlYLre59ot+v3nP8zvheldIVk4gwzt6KjO2wkGbt1+H4H0Wg+9cxP/bHZ3l9JQIm3XfpRHwsDvksnUZwRA64V1zFRjzpfcjqFQ9/F+gVbMuN5n5C4Iyam5j4idtOIzDlKEkblgqnuQxayfc8Tod0rI+bpFg9LeK46ZKRTXA3O7DMXQ1Kfo2UWkn6muqFrPKWWJJgWQs5V6MqVWFew9DGRzSnI2oCNQ4IoQsXicM6BNS00XeBIkIxwfPLWL74cAJc+7ANaeSWQUG3JSdFl4g3Wrzgdr3FkGhph+cQLx4OdX8nNfj1+Rtx/5gd9X7YAPjE9MEt27ILYWdaA26kmq00LPGg6NAlQL1hTKYcuJ1EZhzZnI5C+Ah2choGSTMWAws9BWnq6TyEi1mlYNQ1SJcimYhIJlBWcTlJy65nZhkh2OKdibtumzbi7rsshK9bjLlVdEYs+B2JKx/bYpmBXp0izoFYxG1GXo7Jo4dLdPrxpseRf3/jlNgDue/ij/mQF2CVFshZCCFVaFsqT6AG23qUUEY9f+d//ZO0AD21+m1exxHjZ1tSzOiRHmjUjyF2NxjMgMCJgFnVbzn5sVtQyED8166GANAohHCvgtNLMXYEUIfsPJVnM0Kc0tmQqFSNRtlh/D8UeEmEsvXhIY5bUYRtwgoWr6eoBqlkRyaALsDiRMFIaZw3CFRx5R6IU0HBoY7ZV3NLIOjqhcQIRWsnGcXXvZbTsHff/uHeuYIOUQzunMDFv7g15opwgTMVSZ2xHKZ967o9Phv9KdhxxYuPd/lTUYe4VW0oxb3I6WFaB70cgc6zotHx/SxOP6bjwf8L2KvFI9l1DozK2pePQllgbtd/3KhR5rqWFp/EIJQzTpmGkgjBEY2TKfV7znDJ0qqptzqysbzuRh8pzxqUsQm+iylmJwB4GG85wraiLRasfmIuGTiClxAlVvkApRY1gr1pwKhu3ieaVg8+0AfDGix/xa1qz6RxTW7Lwip5O2mQxt449o+hry2NXjgc7v5Kb/Xr8jLiw9a0+tQKdpZiywEqD8qotqcKO6V3A72FfOkYhEw+9duVBhBawQ5mXiR9CifYBb+gQOKLNC2ZCkukOZ+N1rtV7YMOOotFKsC5SboeOYiNYk44jUbdEDClgYXI6CpRriCwUrkYJQV+P26C6LpboRtANFHIBTdRh2SyJXM1pkZBLw8Rbat3lYPc32gD49osf8aEj6Bq44las6w6ZUIgk5aAoEVHGmz38zHPHI568Hh/uK1mTuH/rW3yFYVOPsS68hUs6qiY3FqFiimbJmoqYmoZlnLZ7Q1yETt8QoRTOeYbSUwJR4PBbwVIYulIzsTVxlNJzPfainCEhJZBo71iZBp9tMhSKmT2g4xPi9qSRLJHo0HXzCueLlj7WUCL1COVqRq5gQUg8HSMf41XCvFmQiFDueUQ4lqxhrAY8O/nNNgC+9+IP+4kKeIdtA1wrz8IW1ELijGCUaCJh+b+fPR7s/Epu9uvxM2L79Pv8Vl1RhvarSjGupLI5yhmqcPDbnE6UUYUOXOjSITiQpuXLhbyh9pYMTz+0ZP3L4NHIeQ6xpDpj7hwXZEZJzQSJxXHWWuooBFPoyyctmPOUgC0ZsSNWbPqEQCGV4SwHJi7njAyZf4LyFYlzrETYeUSrQWx3gVA9+KZt3wYwp+sckYh55vCzbQB84wMf84flin63R5GvuOAbnlERa95xOR5wUxiumZorL/zPf7KSwHdvfau/IuBR1eeaXRJA1TrQtE3NIhAlXd0Gwkkb0bQMlOb/216TcMO9YdNHREpSBzJnkHSFcz88uEgx8Am9wPRxBTNXk4bk0SsaYbCyjwoQq7MkCEodo6TE1hX7GDICbu/Iw84iNSd1EI9WdHCsvIdAG7O+JZQ6W7wsTJGesAc0SDpK8dL+ywFw8eEP+AtmHR+qHAHduuZU3GOmNLkp6QrP1Fu++NK/+JMVAHe6Lb3n7A/4wPU3TrT8NMeSIzdkHMibouCkE9TecKQ1G87wO1d+/p7e0O+7/KP+uaZuuYgBXYidYxn6CtLxVt/wqUaxRsFQJGgVUfiaRZQQV0dc0AMqWzORCdJaNqOUpVkivGQVx205WXiBCuIX3SOvJgxcRq4tU6U5XzccKE3hbKiTWIsHzGyN84YtMhb1kk/feH0fKXf8cN5+6t/zmYrYU45NEdEYx+nwplUlKu60cHIRauuA0SvNZ67/3B3/jDsJyvdc/hG/Ug0nG0XRdv8DtiBb8cl1VXG/7LGsCvAlfZ2xtA0zW7YQtgaG3nHgayZes0HZtoz7ukta5RyKnH60wUQ3nCMjtznzqEsU4GztuZYviYwg7YzRSpGtblGrAZEQ5MqwExuuPPV/3NO//53cq9/vs3e8uPPnv9efDX1zLRhVgi0pmLsK7eFIeKK6II+7XNIZ+82Cz1+9tzvAN539Ad+PUrIg3aqmvBDFbLqAH2jiAGoFMmqghHctG5MgROnT9SYUmBQipgl9RavoSYEXMUt8i0G84Ka8myFXbdjZPD1jkDJl0y3Y0YGUothwklVAJkVCZnIilbHuK1wcsIiUXVvx2Iuv77LyjgPgwsUP+DPhYVvJaWlb5G6lIhZmQeICETyQyFKclC2i+IVrP3vHP+NOovrbL/2In7dcw4qbddOikaeCHiEok2zdIntxaBUb2m16oSUXQp1glhzIPt22F2CJjKMSJZ1kxE4+xXrLI3Gfg7bhbNgQlheLBRvpSbSbtcSSYWhgNUty76lCeusl+82U+/onmZVzTuk+H3/+9d1YuuOH89b7f8yHZsrSr1iFB13P2RB9vCzZISKrD9mIA+nCcSQ1V67d29bqN1/6oJ+WK85119lpcrrRiFl+k74aEumEwybnolShouQp69kUjrLOcUqA6rW9jjpLGM8NQlV0opjSmJYZ1rEGrQPlzDDHc8vmxL0TDOsVRfi6E2hb4KI+V2rDA4llvw6t5dDo8tRW8PhLr++q4o4D4C0XPuC71Yyj0JqNtzBmyn4nRliBt4ZEdkgC0hZk3SLmqSv3Nqt+24UP+cSbVtgx83AyYBQ2x7c+A6IVhHSSbbTQuGKPKOox0CnP1wt6SrYNoZA75HlOqhw29DasaHGB0yFhJCSDglx7ijpvK5eujlnzectP2GygijSToH4mYs05bqXwCKEENvzaH7cj4NEzH/AKg6RhITWpq5AyIVb9VjgxwHBNpiSxYL1xfOrqv7zjILuTI+Dt53/QdwPW7w15nNIXCmfqtlcQ2tFT6zjl4LZQrEnZ6g2q2tNNGqTzrYvJJNDRgmBFKOqmptIRWbGiiuMWwwj0tsIY+hpqr/EyIg3q5XLR9iq2siGH1YKlhJ7UDIzlKLTGHVy7djxV853cg7v57B0/nK87/wHvpSS2Qbc3pq6P8CIoZpK2U9gLaGBABeMOfd/wKy/d2yz43Rd+wKtoTNcUzMySqY44URfsJ32mZsZAdEhkygzJ5cAFjDYZuZxNpRkLzX7ALcK/TjFWkue8o7Rl20TqB98C71hq2HKKoqlaNrR2OXPTUGjFUbOkr2MiF0piw+1EcbIUbUBuSM2v3+Md8G4efvjuHQfAo+c+4NvGrMjQkaE0tqV+SZ1xUBwQ67ito42IMb7mS/d4B/jON/1XfmqnZE0Qb/ZRdcM1adkgoSpzxlmXVbOiE61hZUFaS17QDZlLOWVnzOM1pF9gq2Be5drSVUcRJxBMVkeorM9B7bCBo9Ad4PIViRAsGs+adtTGtrjFXCi2kjG73rJer/BJr7We+fhTf/gysjsJijsOgO8+92F/21V0lW+JGeeV51lvKcs5XZ1RSd9y+32zYk3EfOL6vd0C3/7Of+Tvt0smy9228thSCS86zzkZUShHY1ecbCwzlfKcr8jKnDdkF/m8OSRVmne5qpWz3ex3OT2FDS25LmpW9RyJY10IlgGUkgnCOnygsnW36a+OKGLNKWe4oToEoewJ3WVlc46s52TQNGYJP/eFv3bH9/hOHuDdfvaOF/eeC3/WH9mSZezYCMyeACTFGYeVQmpL1NKxFF3tyV3K01fvLcPmkYf/E9+xMzpec8vWXFaKZ+Ka/tJAgLKjlInrY0aONy9TnpSet6VnmdgJHWYclJ5FUP5kFRtLyzCWpDblGjUPx5vcaCuehjMJzKsSbWpWyZih97yQ32QQ94mcCERkui5wEQLVLedWveC+6AT/6oW/f8f3+G4f6p18/44X9+7T3+OFj3BZB10vscG8yDfIQBMfnKfxNTeXh5wREN6hx258/I5/xp38Bc5f/qDvFUVLVAmdvUpGrVGFD7W/tywtL9PBA+nJlsQuJk96DM2KGzr4GcIJ55jYhjIbI42h8J6eV5xLYmZWcNUrOqJiOxszq1eBs0JOzlCOmZcHdLWmbysO4qzlNOh8j/Wox0wMeeza61tEIr755Hf7aYBJ9ZCOq1jzK27SsAyU7iCskJKXRMPQSBbOMdQxtXd0rWVPQWoUpbKcDF4/ruawBYQ8mzpqZePhvFy1II1lqWQLFw9kUPgEFlGNNSU91Sd2DUJq8pYFXHMUNPtpxmbTtHBvIJHviog3+YgnhGHdOHSc0HeKqS8owlmchVLPQBIRm5qJhW6sODA1vbAOnzDwK/K4x+nAHCqDBHyBCyziUPs7y6C3RVkvaFzEMu0yriaEgrIQHSJ/1FYOJlQdWrJO0EEqbtZTzqgBQ9UhpyJXiptVzoO9+/l/nvu79/QFuJOX5fdtBQe27K1mRZKN6diC27XhXBpuiED6gMSF80+0rdWFaEjqpi2Z8izjRFVzKGnNHXpK82TU8GiuuWorEi3YizR9Y6iNoRP1aMyq1dpFoTHjPHVgf7iidRSbVHV7lCxdTU92godowHjxkW7JH0Y1bW+h8LrtwwfUMg9MdKXb83fXFwx94CppBrak8Ckrv+RMy2CImMQJZ8Pv1YxUSSadLbbnOyzTDGcCd+BlKnlI6g6JuRhrbsd9zlvPjWrW6hWm2iFNwWbUQ1LTDYRX51AqwlhLJWNGgYq2XKDTTkuH++KLr2+auXj49Hd5F4+4oDzTekEZCBNOMZcdTmvFrllw2mfsSoMmJ9Kj9jNFoI55yyxWbBWmfbu7IqEM2bAW3KoLslgybCKcatgLjB5j2RMRm0FoKSO2gtiknmOV4JTzXA0qYGGphCAJJNPw7tQVEkmSKaZ18CAqECpI2QQd55kQUSpBYgKHoWI97bY8hRuqYrgMgVJS6g4JDWs+yMcSLilDIbqtWeWBjqiqmkQ6ZOA7ioALaGobzC2G1H5J3QTX06ZtAJXetjyGpLH4SLHnYNPVFFpwPgBHPqZyq3aHlJ2Uz75wb5Pgu94B3nXue7wWHaweMLW7lMspvaC8daHZA+NoxCr46TUNZwKwonpcDZi8DrWzoBMcwERD5S0D7dmvHf3gHBoaL960yqIg3AxASzCHS3SKslMWway5WRLHfVold9jqA4kkiFCF5Jqz3B/FPG4tY6Xo2JI9a+g3rmUAn4wibvqKOO0yKCsOQxcyCscT7Mmcs3VI3oIr2aqtThqg1/ILDEomFIG5VE0ZZUP67oipiElcCL6USDgWaLqij9Q5VRU0gQ7vLOu6x0EUGCg1a9GQVX3UlsSNrXgoXW93kRerOb20zw6SW/cYC7nhwGnYAAAgAElEQVTrAPi2sz/gl1HW+vFNwrbrPWtKtcnUbRN0dh2GgV4R5GAKnveB+RtTBSFICBKv2NeOjtUYZ9nQmkkjyXXOhlV0dMx+2bCdxUTUHNi4lYSNbc3Vr5yjfbJW9DEN3sE4HkWw45YINWpJoS5J2W1WbChPZYIjRrdFHk8iGAQ9IZ5NVzBvDammDESXPNDUnCCQVlxgF7UKxrL1GwhuJv1wbFTz1oh66hpc8BEsF7i4g7eB82iIWr7AskUPpcw4aArO6C673qEDAV5n1IGKJgMnIIBDXWIfjrcgtO2yKeBXrt9bMOyuA+DR09/jw1EcDBSOioImgp7QLOqSStJKr6XuIH3N0hh05KldylrwALYFlZLM6orTOgbnmfkiSD4ZyYh9U1NrSbfxXIo8t12Mkw37zrGeDIjLFQd6yCPGcIscZSoGnSEvFCsSacl0r+1V6fAQfdVS0SOX4EQBqs+omFGmI5Yh46+XtAIXNW55jIHMmmKY65hOVVBKSS2jlup2Knn5GDtqFhQhh5AxZ3zC3C9obE3lBWejmDzkK7Vh4as2iZyGoHOG/aCX1NANHca6Ike2pJGh7BKE1bMyZ3uwTd0U/Nb1/+v1nQS+7dR3ehuttZSuqgkwqGkFmD4aMKhX3AiO3cHGVXiW1rEeD5g6QxSwf1u1bh4hoauC+6e1LQlUBS5hOM+d4SXjuC/W7ATuXxQ8+ip6dcUl2eOxpuS92ZCrTUPhS+Ig/MgyjkL+IHvtQ9tUXWR5xK5YMdIDbnlLz9cYHXNYO1IVpGmw8EukVWy0FQPcDnpCG1JJSeUsb4oTrlNx0DT00lOMXM1OMyWwDq2QnBJDKrff1vOTyrCVdhBqyOFql804QMKaMrSGjSVNOozav3ePQ7FsK47AmYyTpPU4DghkAJziqMdj1+8tH+Kud4BvPf89Ple9Vub1WHmboco4srCpB+R+SuwdkYyo4xGLxVV0PKBL3foBBgrVypRMQ9eLqE3g5qLHtst5zlRsaUUc95jk+5xON6jKOSrpcruesR2vU9RT4mxM7CQ2+POQcBgSOfeyNOxWb52HasdLbt4mZ3m1Yhj1WtOKSoZ8wuJ9ynW/4GRTszIeFffbslA1Yev2xCpm5gyNkIx8OOPjlqreU6pVJRd1027dS9VHuzmqabAyYk0YSjXCNQetKCUXtDmMDMYVDrqdDNMoIlcileOKU/RUhq9zDjLD2ULjOj0++8K9BcPuOgDeev77fBR1qZykNlNOhdanhSbpM6lWnPCWk9GY20EZ5Bctd052ethixXnVYSnDid5ww0SMdMR9JOyYA3KS1gSydoZesk5ji9b69DYV94XcwSlEd4CxfTZFya7NWdUrVBwhvOXAONZlQqYj9lczRpFr1xgOmMDn35YFu1VCLx1w1MxJXcMMwwOqx46rWhXwSgdSqmp3AR2yd2cQ6RjhPYNqSdUaSMqWftqie/lRy0YOO15jLHG2Tc837FQTuipm7jVdKVtT66AmWsvWmdZHhPQ1WOUsZJeRnXMzFgwqQZr2+MLrPQAePfdn/FZvi6vlinVTUEjJNh22O2s8n0/o+BqyhGdDCWZaw9X2Ld1Og2/QjLjxDJRiVnmkDgKRgKJZjgKz2Clq6akixbyqeEgGhL5i4jSlq4i8YKtzgut+xVowikRxVEzJugOqouJifxsCpiD7XPVTkrLmUGtEeLtFQqHCQxVs+JhZecRIK2aqx1xZUjdHGtceUZWpW+Vw5SOmwrDRGbNazlsenw60994Z9s2ctSpnV8dkQSurJCfj0+z5I/q25hYV91vFiz4cgwmV86SqS2VWxMoiTckw3WYWvBGCr0IckxjJ5268zpPA+09+l7/U2WIeRJWy5EqgVOlBy4AJVrDLetkaJ543tgVNOpRYYxjFmmXQCfiaSAtWIuF0+TJpMrYLVi6lkpb1OG0Trm5wG4l7mGLeqnGE8eRBoilyXBBnmhld60kH68xXJTJyvEUOeFIZhFEM7ZLrIuJhmbzsHIbnhllwLh1wUO8zjkfslMFudsSRWzEwDQTpoClbO5vQ0cwCdSvqMiUjq/YxyuGtZF11mAmCgV2oR+ArTa91vd4+/CebKeO4yzAYXYmIZVBDxQkngotYkMOHxpi3jNNea+K8dB5nA+G05ne+Ik+/2636Xn1ffP2F7/IrOvimJBVBNx+8sRIeCBTp1R57KmkduU6EZFDSEh2HSjOzQcdpKK1nS+rWISRWDWVTEarhUTJuefZxE9i1njjKWFVzVDBoCjc4GEeK4BWkWwr3ej5rHUZ7cZfbzRzvDNt6m9v1hDcn61w3E1QwraLPzDVclp6bSjCMNlgF1p5U9FyODOe+1bwkHWdFym+8dDyH0X/bDf+uBz/mJ6ahT0NPp8yFJS8dn/4jpi0U7z/3vf6ZWDMqSg7qmrMKChnzgJRcdaF3v8KpAYTuma3IbUVGqOVpzRYWSpKGLd9W9IkZh1o/qH9V1pZXBPjUGzJXtt2/8BY1zgUn1vbXQvdbB/F6NW2FJWshcOoZHVcTBdJla/USaFoVUWskJZkjGKqIUOHLeL3tB4ylYhIUx82qlbldkxVrxvNb9wiN/OZLP+pzDdtGQxSzVy+JYsWnn/knr+uy7/cGtnj01Hf4eXqO9fIaTklE49AiY6Eq9q1p+/xbusMN69ggUK+q1itozVek8Sa+mrEUDY1p2FIRwT5gbvJ2uNEkIGsy52RZtEIKpVL2qkOUHHHCBQpVQifpUjRlqysshW31/5WfE1tPrNfoCMVeMJGwJVWc0m0icuVY15ItEYPYYK4D9FuzDDu+Shi29vOSwjp+9x4FwLvP//t+5ffZEhlVmIkkA0AV84tX/qc/WgHwwMnv9KNoC2sOyJTkevD99ZrEKxa+YC3w/YJ7ZzBPrvK2m6d8KAsjpJF4X+NVxNK/jLi5FimU7bCokdMcyJzYOmZSkCRr1PVhmHwWGsFY7xGqj7GLVnMYmk3bep2DMG9ABM+KjJWPSJRk4E3bju7HaStdS4OjeLlga3yafUN7jByt5uisR+ngzS3vX/KLL94bOPYbLn04dB7oNoLNNHgYhDyg5ndexdlG/7Zj6NX4/+JNp/+0JxkhAmM2kBhFxXnZJSHhSCzBBpu0IV7WVPUCLT1H1nFOpxyJDoVZthZzJ4RgHrJtMq52FeeaMA8m4VpzwDAkcTpDfKUaCJ097w2V0JxVXZ70MwKxMzSQMtFlalekokHGY1AJHbPEmYYqSL7C7JxQWnYCkCSIkwFnbcNCp+TCcq6quRbIqqFd72p+99q94STed/ZD/p3xmM8p2obYfVIRCsJfP6ar+avxMI9zDfHw9vu97I6oA9vFle2kz4nRrbAyDG48qTWHIm47f94u8QGiU8ESVtOP++z5gqaaEcseWgiOsHS8o1JJCwYF5/C5d2wEFo2I2jExLsC5dY2II1KxxszsIKym9jm9dAxVcAvxnNRd9qNu6wk8kxXrRjINa4zWQiZBGkrWZIOiKbiKYyMojKMh86ZsVSBBOPrrV+7NDvCO8x/yjU64X/fZrUuEdtxygmde+B//aB0Bp7e/w59MOohQpoUGioITesg00KBNa7GIjjdZNTNWrqAh4mSsmdcNpR7SiyzlYpeF1sSkRHFMp1lxFMqkaMCqnLKqLQ93Nsl8w9V60nbaQoW2Cvi5Spnnh/jIt6PmYhGHmoSpWRJu8FrQ+oXdwU3Z7Ur06mUvoIWoWXcKdEoWZRw2HpEq7itDN9JggudxXfPZK3eHx7/njf+ZP2kcXqfc8MExzTERHU75AmzoDTacVTGfX85IsoyRFJxpFL/maja0QuvASkqQbtEioUE/sRFoa8IykJZPPP7aOo+Lrzv17/oqEqwZwzSc4cFnT/Va0UPtKpyE9bRHUSypzJIWOg7GjlGXJNpsGTDz8rBN3lJpiKRrJWKBU5ClY+b1ipkPWXvCGHgiNH1saMzEVFKS9yI6q4rSNq1LZ5R0WFEzNo4daxh2Bu2DDDrD0ANYyYQkGrZehFVo9AR7WR1cQRUbKHaEbX0O3yx7eGH4hRfvTp37Tec+5sXgBE05YRgFh7QCFa0TzIumVaDGw4vNlAeiMdZLnh6v2FqOoS6JAkykeuRNzhs6AybFgpup5oEy5wZxW07//Jf/5mu6Y3zNH/6DD37MF9WSq2mfvF5yWXVxvuRXn787qdP3f8Pf8pfTAfPlkrnM+Bef+wt/4A34vgd/wq+aKUpE3DIlHd1FRAlZPaFUKbeD8ZNpiFRwBxV0dMbCwRktmRb7GN+l0p5BktIUJYmOWVDSlV2GvuKqFcS+IZOeoe5zVJfMVZCQ9dg3S84la63K6IZKeIuU3DKz1mM4GEvkfskquJIGM+w65D+ehRB044ij5QqTjHmbgifqOegh2k25LIcsYtir89YA+1Nf+sOZP/S18oOvefPfeN8P+y0R5umETuCQebVoxRJP3OWZ+o63/U1/XzxuhzXcrPf4hd/9z//AAHjb+Q/6M6rLLRHMKfucjTyTYEoUPIF9QyCo2kDuFF3mbkVTNmRZv51LHGzn0jDDWNL6AF4SKVdl1eYrGy7ntvcErtDFyFNVhn1RtTSvoBoO+MI3hfrebXAoAwvItNYzUw/nwxzCwFOwsq18lnLFxfgcR5Qt8LRXwkY6pCcX3F4Zvj49wQ05R9ma2wF5DHSzSHK5Uvzsl+5++udxkr+vfudr3vz3X/oRX6vQ4avJZHACCe7flheu3N0O8MF3/G1/ni1+u9PwoPX841//sT8wAP7UpY/6PCSTAYRpArZQEeseRwFcGqREHcHm7RyvOhyF9pT3bImIA2HYtoLnO57zed0KOCdNmBmo27kFc2PbxpXyZcsiCtOvwxBZIxSndMasOGxbyVk8oIwy3GrB9UQycopcR5yrCxIRSCYNvTCESo14Qr7EfctQCr88Kq/TX2NVT8nUEOOC7a5llSY4H+x2FSUNP/fka5wDfK3oefvFj/i6TWLCcGdJLxgpmIonjjm966s/5y+886d8YB8ZbcmF5qd++z/8AwPgXQ9+1BsThn8YKl9wzg2oI8HtQB61DS7KWE/HzPIjFhj6UrZTy6xMW6uZHRNYSsF9KuJsKrlpgpIhgDeWTaHaFu4kCDmU4IZM2LCeroIXg2WtKSD4CaR9epEhNh1WKqOu99kwjicouCBSbPcUZTlhZEuuNNCXq9ZkYvPEg8z2r6JkxeMuCEtWXNS6dTUN43YnNHzyqdfWmvZr3vz33v+jPnDtA8dt4jxjq8i85V/f4RHwfY/8Jd/zAWihNZHqZjFJXXHgDUlwEg20qzB0Qmm+HMe8pZFMm3krzCxDOVrfRMQn2DCH7LgwbyjMKFi0I2aSKEwiq9mVlhPRkJl1DExgBDcMnaPpjZlURWs2WdmGcZS1JlWzYsUjSdIih1MShsWEUhSodK0dV3/bN+31rlRH3Nc9j1IdzsuaG+kpzlvHS/UuaTVBxh0WNkL0zjJwDaeaOZ+pp6yQPDgc0HFdbh5dJbhYHTaLllPYCRyJkAe4JdIZPv3ca9s6/poB8O7LH/EqitixjmEgZQZf3VLwqSt3Bq78qYf+sg/Tw/P0iLHutnDpGdVnL5Rps12yUNM3BdvJgNNO8qsYelgyEXEjAERml83uKeZ2jm8KEt1tgalQseTBxkXHXPa+ZSxd854TWcqghqvCMA6mkY4WCh7YYGytWQs0L7NgIoMaOCHO+nypzjkVqPE6Zlov2Y0Uj8ou+/UCn24xE4bUp5zpbFKGnChk+IkjKmtiQTtcam+1x7M4LgaqmhJsq4wdH2hwgeWYcFrUrVDkXb0xv7TcYUzVlre/9tTfeW2rgIcu/IhvwjlZ65Z3p4gJZ67AMiQOroCgRCt37tdVO34lmD9sGsG+Fq2DZ5rEiLJuadk66rJXzpglMQ87KDbewHpRtSBN0hScXF/naSIuFg3baZfni702w48dHIhgAzdrcYURfXZ9SVTfwsUbDMOAqmLSDpPoqGGr6wu9/8DyvS8a45uap8hbW/m1uuAwDTMHgoNYzbj2zJKchY3b+cTBtzi4mwfz6fNhhmBTttTugINW1YKFTDkZYORYc0DCO0XMQeh0Bpp4M2WaRpwswnCKwFO0dOItjpppi0GkgSpelsTpAB3aqFVF3NEUJljq14yTTa6LfmusnaoVH3/6NT4Cvvnyh33eGISQrGVrjKqcF4XhjIPHvQmvDxcrOIj7nKhXlAEBtHUrg0qamm/UnhdszJFdsa17rQFkEyZvhZwhUmzFfZJkTBrGxCbrTPPnyfQWkypYuwYXrvDGFryUah4OgxkVbOkeN5qaIhi+5bcZhOMj2ULWB+zXMzZlB5kkrXm0l5qj2DOqlkyiPlvBtCkeUlZHLQsoqI2QKakoEbJPXs7ZVIIXAiDRsoUD4hk4DRGBIVg6z0bgOASPIRbcCsdPE5RSlnq8jasXvLHMueUFSay43njWO13SUGK2QyuCF7IgivtQF8SyCtgle2aBF4LNZLPlDM59zkO+y8dfeo0D4H2XPuR71nMUh7cisIESPlcvyZTB25hRKimrirEQ3NIpkdV0ZIMKBFFpuBocgcLsAKG4qjwPuoSJCMSKcFYf0F9/hJVIeXMG1/ZuMQ6NnYDh17eQyYBeFVhZji8GM0ghqJ1ojSZtGrNTC3ann6eTbAZbaVwzRQYun1SsFwWj4Ql2qwbrwigZx7YOKt0B14WFEDhR0lLGL7pgZFW23bxN5dkPwyxadnDKkY9QEoryACNgKTpsiBV1a5bt2OquY5qarcBJDPayvmxHyi/KgjJMT7MO21nDLPfoRWHXWrWSOqIhvWZB6eEWKcqtGIaKavMSZV6DmTCKMj7+zGuLHop33P9jXgXiZyiNnCeTBqkkO+1snxgrJevG4mUYKxV694J9Ct4oB9zEkbmX3fkDhnCtWDBMUqr2v0PhLecHD/NgOuTZfIbwKRcTwxedYzvw/AZj9ouKiIprftViCcGc53pwJY27vKWu+fz0CZbYdvLHLQoSVzEwsK7idlcIlLV9cmwo68K4WZlwtbvOw/NrPO2C0LNhW2TccsGqVlIGnmEkscWMZZS+bHUnqlb8kcq4VTsdiRjhKoYimGANOJShtFSciEYsm1k71Tx0AJfEdPSopYudCdKwYF5rJbuiZNDdYr2aUNs2B2TNJnxZw0ZvvRWTrhY38DLh08++ti5i4psu/7CPdUIepm4Eu7RY03hP2QQ8JfTVPad02lqwb/iGuYc1IXlWq3bs+wnreA5PHOxWdMxF1W1ds4omOId41tfezKEPN1PT85LChV6+46Uy5qw95Lo5oBePmDdhPFvNoauRKiVTjudXBReaa+zboNRNebLKGTvBuoZDmXB/vMETbkFdzdoEsyV/Jkk72q0JQk+f86yPOBHmCDnBgVgxcz2S4DFMw00Ma8nGy1yC0F8wpq143hWP23XcDLzFzjbbXvKirXgwmD+GppBxTOsdmgBFdC8zLHdQPjSdRDuLeD1eJ5eiVQwvTHA5LRlG3VaX5IPSOEm4XhySNZrPXr+3Hkr/tiaR+JY3/HlflDMI7J0wOiYOg54MMo0RxZw87eDrJanqMRCOLwYyhg3K4WCkZFrELVQLQZTZcwJvSl4Ulq4XBIHNeHCeojGcK5YMsy7PNo73nXwzv7y4QhRkY8U1LkWneX62j+vHdFTGotzlIWN5Me4jyl0yrVkay6wu+H/bO5MfO64qjP9qulX1hu7XY3qy284MOIIFggghIVYsmIQiJCQiFgi2/GfskBBsYAUoYkESHEiMHSfu6b3u1/3mGm9ddG5YIjlSlKJkat1tneNzvq5bde75vq9yS9YtrSzH7XZZI+JBMbPsHDnTBwyIi6G9vZRH9qDKGFqdv4pLMaHwheur6LoKL8uoul36umRUFvIcIdYlVXdAlkzsQssL8TapSNDkEwb9AztbmBRjOwEcVpp+vIswxaaLK6Y92FkWHHS3KZwes3zIXO5WHGWZTmt+jz1fVmwyHpa5/Ur5y2ecqzytwU/7uXPv7k/NYdihKzdeJsetQHnwWB6pjmtv3s6cjEQYMPL2jLh2yBeDPB/EXcsj1XPLp5ejQ2gZwtOTr4hACJ7959jXogiu2PJzFkrxhfiIh9cnaLWw9m9iHztxNK9EL/MuN7y8GlI5PZ5QcTb5O4frz1sRSOEgiu7DQJYwfTgyiisRqyoz2wRXZ/S6GzxajLklVjKyN4DP1BXNYzG4EB9D2UuGWOZ+pUaHHWtHMxTFU5Pbc1mOiYkprYlFv/sC607GSI4d1cPkc0bVgttRzFVestzc53g6JclSa08XE7LRW+NR6HE8veGimFmG02U35ktLobt1ORWFUqHSu5p3Hn6+MnpPBcC3X/6l6Ysrp9inZh9R+F2KcIPj1djeusn0b6oTtlWAzkUi1uN5P6cMIubZin23b2nR75YLay4lQx/ZFch1YrV5D7wDLkTJM/2QKNi2LmRp75jJ7BGjEF4vYk691DKRjtU6/XTBI1nrWnzMfSGcLD7A666zWF0SVh6nMTyXhmShx1KtsY3mxSzhLSPu4xFl2OF8NeFAdhJKkL0U2UswSrEqNKsyZ80N7V2BvAsI1eyqXGCMsJ9KdoIQTCwfP5xkE94+bTa162kNftrPne9/8VdG+x10GLCaToiVjEM1Y1d0c312O7eZzO9zkxtrIjEIupy7JUfC/K0Cy/6RNe2x61iBpqC/T7QYMVUZ8zxgyyjuBj7vicS8jnncSbin7vB+suSwTK3viDwUJ6rPjtonXZ7xtnPOnqNQecl0eUKpeoxXl3iOT98X4+kLujrgJOiyG8FqPrIz/HVZU/M71kBaRr3CQii8wP61F/Em5FOmIgUrSyyVx0Do3qWDZ1aW2iU2NG6ZEUYb3PY9PkqvuX/2+//poOZpDfysP3e+9dIvzPNBz3LzL7MRuyq0388y/586hlBtkyfXdqAiZ+WFgTuWBxBxni6sX/AiHLDpVYySkQWMDGwO5IXRk+3gLaIi5dR1ec13+JcXsxfcYlROUGJWNbtgPRrwkIK70S1O0wXf9Av+mie4nuFk8j67QQdyWRDpEbiaJ0WO5+bs0WdeiTRtRk8UA1y5u+gzLeb2xS73DKqQ/UbhA4j7UcjAyKi3pG9fTHto1zAVDgGZNZLOtLZ/BItsReJonvzHcuazFrqp/975xos/M+vRHvNCLkcWnGdz0s6A7nyIinYoTMVZOeEuHuMiQUdys5Vbr8DEyS1VLFRbVHnCXRXw0OmRmIVd+hSuYGo26IQ9bpVL5ss5406f2A3YENEGrXngwJfDPiflhBe82zypLjjLU74B/M7T7MxP7a6/2MhsqgEfmZydNOFEzuv+Jru64kk6YyF8fn/ByhlY/6KVLqwVnegWigexaPmJKZZJx6QqIiq0dSinEDcEkcjxSUxi3ceFw3CVTy1h9vHoz8/6E+BNo9SWXbZ8Yhw28oklQMZCk1by5SzymFdcmpDIGi5W1q5tSYEcHUbm6XJXYPl6EUNX4RRTxh6syQqVs8nM5By4fS7LEWF4yB1T8rd0jgkqe926nXqs4oDjrVd5MHqHdVc4+wsyrZjM32NLFLuNeBVF3LgxXj5mbgzr4Qa3XIdFsWQpIk++YjfY5VKPrKRbKt6DuSb3KqLec3jZDCdd4oVr5FVK4HW57UXcT4f2ZXZcJWxWkd1CFr0CMZg8vXrGAfDVl39uBqxz4s3sN+pGkcnD0FrBuqrHHQeGec6HRqxlc+sH6Lsy1JEiVXSikLTqoPMxxdoBu9qhLER4wWfkpPg6IvEiullCFIdkWqzoHe51Qp6YCK0r7oosjE5YhYfE5YL3i0/kZYwJGM4fkukpPTfCcYSACrNkwlCUPTs7FOUKv0wsBdwPelYGQhRJUtEENCUzTzEwhoHftX5Dsik8EltZB3qe4lT5RIuUI5OxEssavcSL1oizxLqlPLr807P9BPjOC2+aoRKJE+HIl4yM3FWX3GSayIlFdx2nmCAS8ULxkivdDRVS5FNyawC5pBftcZCXnCnf6gucFTdWZ0f4An064PeJhFVUzLnXP7KM4rQoiVWfD7TGVIX1KnoxPGSUntvNowUex7IVPHmbuewnihOH49JVEY/TG3q6ZM3tsQpcitWIjtfhuhfiZSG3ixtrVxMWMFAiHp0TuOJ8nltfg0LuBTREvuJCK14115zYAVePMooR14tJMbfGeQ/O/vBsA+D14x+Znc4xw3zEZaG5E3bINOwZkWzpsHIT6wKyXWjGUZdYPvPyiV23EvUOkV0vna5dBhVVDVkevXAqPKXsDd9bn1El6+vHb5hMnEtVxKkDh0bh6gmXprC8RBnr7HjVJ/qApiL3O8xlrl9UrEUbXInEix+SOSG7VcnCLSxjSBxONwJl5eVH6ZLc5OzLSjw+gXhluRo3S7g//OMzDoA7PzbznSM2plOryCVunysxkfUq9r0B0yjndHZDblxCFw7DvtXJz5IxC7NgRIfdaGD/omQ6OJGFS1EMFx0dx+f+418/0wVs6tv9p83LOTp+wxx4oWXYFKsxY+XTEZq2XOgQYVwRkJxzXV1bPZwddYyoBF9nQwK/Y89e15cV8QGxk5Kml5ZbH8vc33ifu2PIp/2Ptr/33yvgfO3we6aMBqQiByVLDcKPD4Svv6Bwt9gKHN5PznCDLkdZhollKWLMVTbHD4TrN2Wv/zwHrsuwnLF0VjhFSV/EE9KCt86bLZDw/w4M597RD829eI9/lDeWnLFTiKnCinkwwD/Y5+AyI9YrTvMxWm4F6bDML61EihuKXk/KnvHpD57jPE04L2fWbDovxUXc460nv2mPgAajzHnpzk/MmieyC3BelWxUMgnzKAqPKz9m38nsVXFaiP6vh1HbzNIR54ksZzjQWedOoVHbd6nmV8ycBJ1qXOWxzFP+2XCZtAb3ppbUnNcOf2B2hfqFeP6krEroeh3rnnHiKpRZsWPZMh2SqqIbhFTpDX/t88kAAAGSSURBVB+zYsNdszt5r3R6PC7XcJ05pb4iK8VDK0a5/uduG1dLlZ7hIM5Xbn3XeFVAL960dKvHbkhXF2xGA/zS4WE545iSkSfEzzXicsmFTuyL4oZe2EFLbnrsb+7TX11zKTQonVBEm4yzGx6c/rY9AhoMoLY5DW5OHam1AKijyg2O0QKgwc2pI7UWAHVUucExWgA0uDl1pNYCoI4qNzhGC4AGN6eO1FoA1FHlBsdoAdDg5tSRWguAOqrc4BgtABrcnDpSawFQR5UbHKMFQIObU0dqLQDqqHKDY7QAaHBz6kitBUAdVW5wjBYADW5OHam1AKijyg2O0QKgwc2pI7UWAHVUucExWgA0uDl1pNYCoI4qNzhGC4AGN6eO1FoA1FHlBsdoAdDg5tSRWguAOqrc4BgtABrcnDpSawFQR5UbHKMFQIObU0dqLQDqqHKDY7QAaHBz6kitBUAdVW5wjBYADW5OHam1AKijyg2O0QKgwc2pI7UWAHVUucExWgA0uDl1pNYCoI4qNzhGC4AGN6eO1P4N4fWgcG6aV1kAAAAASUVORK5CYII="}],"fabricOptions":{"header":"package com.example.mod;","entity":"Entity","render":"","members":""}} \ No newline at end of file diff --git a/assets/models/tentacle.png b/assets/models/tentacle.png new file mode 100644 index 00000000..1026c1c0 Binary files /dev/null and b/assets/models/tentacle.png differ diff --git a/assets/spectral_clock_0.xcf b/assets/spectral_clock_0.xcf new file mode 100644 index 00000000..82bac275 Binary files /dev/null and b/assets/spectral_clock_0.xcf differ diff --git a/src/main/java/com/minelittlepony/unicopia/Config.java b/src/main/java/com/minelittlepony/unicopia/Config.java index c0f86374..3a128a0e 100644 --- a/src/main/java/com/minelittlepony/unicopia/Config.java +++ b/src/main/java/com/minelittlepony/unicopia/Config.java @@ -45,6 +45,16 @@ public class Config extends com.minelittlepony.common.util.settings.Config { .addComment("Removes butterflies from spawning in your world") .addComment("Turn this ON if you have another mod that adds butterflies."); + public final Setting simplifiedPortals = value("compatibility", "simplifiedPortals", false) + .addComment("Disables dynamic portal rendering"); + + public final Setting fancyPortalRefreshRate = value("client", "fancyPortalRefreshRate", -1L) + .addComment("Sets the refresh rate of portals when using fancy portal rendering") + .addComment("Set to -1 (default) for unlimited"); + + public final Setting maxPortalRecursion = value("client", "maxPortalRecursion", 2) + .addComment("Sets the maximum depth to reach when rendering portals through portals"); + public Config() { super(new HeirarchicalJsonConfigAdapter(new GsonBuilder() .registerTypeAdapter(Race.class, RegistryTypeAdapter.of(Race.REGISTRY)) diff --git a/src/main/java/com/minelittlepony/unicopia/Debug.java b/src/main/java/com/minelittlepony/unicopia/Debug.java index 2e5a9982..8cc8847e 100644 --- a/src/main/java/com/minelittlepony/unicopia/Debug.java +++ b/src/main/java/com/minelittlepony/unicopia/Debug.java @@ -1,33 +1,35 @@ package com.minelittlepony.unicopia; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import com.google.common.collect.Sets; 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.registry.tag.TagKey; +import net.minecraft.util.Identifier; import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionTypes; public interface Debug { boolean SPELLBOOK_CHAPTERS = Boolean.getBoolean("unicopia.debug.spellbookChapters"); boolean CHECK_GAME_VALUES = Boolean.getBoolean("unicopia.debug.checkGameValues"); + boolean CHECK_TRAIT_COVERAGE = Boolean.getBoolean("unicopia.debug.checkTraitCoverage"); - boolean[] TESTS_COMPLETE = {false}; + AtomicReference LAST_TESTED_WORLD = new AtomicReference<>(null); static void runTests(World world) { - if (!CHECK_GAME_VALUES || TESTS_COMPLETE[0]) { + if (!CHECK_GAME_VALUES || !world.getDimensionKey().getValue().equals(DimensionTypes.OVERWORLD_ID) || (LAST_TESTED_WORLD.getAndSet(world) == world)) { return; } - 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); + if (CHECK_TRAIT_COVERAGE) { + testTraitCoverage(); } try { @@ -40,4 +42,30 @@ public interface Debug { throw new IllegalStateException("Tests failed", t); } } + + private static void testTraitCoverage() { + Registries.ITEM.getEntrySet().stream().collect(Collectors.toMap( + entry -> entry.getKey().getValue().getNamespace(), + Set::of, + Sets::union + )).forEach((namespace, entries) -> { + @SuppressWarnings("deprecation") + var unregistered = entries.stream() + .filter(entry -> !entry.getValue().getRegistryEntry().isIn(UTags.HAS_NO_TRAITS) && SpellTraits.of(entry.getValue()).isEmpty()) + .map(entry -> { + String id = entry.getKey().getValue().toString(); + + return id + "(" + Registries.ITEM.streamTags() + .filter(entry.getValue().getRegistryEntry()::isIn) + .map(TagKey::id) + .map(Identifier::toString) + .collect(Collectors.joining(", ")) + ")"; + }) + .toList(); + + if (!unregistered.isEmpty()) { + Unicopia.LOGGER.warn("No traits registered for {} items in namepsace {} {}", unregistered.size(), namespace, String.join(",\r\n", unregistered)); + } + }); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/EntityConvertable.java b/src/main/java/com/minelittlepony/unicopia/EntityConvertable.java index d21ac152..6a0ffce7 100644 --- a/src/main/java/com/minelittlepony/unicopia/EntityConvertable.java +++ b/src/main/java/com/minelittlepony/unicopia/EntityConvertable.java @@ -25,7 +25,7 @@ public interface EntityConvertable extends WorldConvertable { * Gets the center position where this caster is located. */ default Vec3d getOriginVector() { - return asEntity().getPos(); + return asEntity().getPos().add(0, asEntity().getHeight() * 0.5F, 0); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java b/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java index be559b38..9f055212 100644 --- a/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java +++ b/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java @@ -33,7 +33,7 @@ public interface EquinePredicates { Predicate IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || IS_PLAYER.test(e)); Predicate IS_PLACED_SPELL = e -> e instanceof Caster && !e.isRemoved(); - Predicate IS_MAGIC_IMMUNE = e -> (e instanceof MagicImmune || !(e instanceof LivingEntity)) && !(e instanceof ItemEntity); + Predicate IS_MAGIC_IMMUNE = e -> (e instanceof MagicImmune || !(e instanceof LivingEntity)) && !(e instanceof ItemEntity) && !(e instanceof ExperienceOrbEntity); Predicate EXCEPT_MAGIC_IMMUNE = IS_MAGIC_IMMUNE.negate(); Predicate VALID_LIVING_AND_NOT_MAGIC_IMMUNE = EntityPredicates.VALID_LIVING_ENTITY.and(EXCEPT_MAGIC_IMMUNE); diff --git a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java index 0e507331..7c60d0d5 100644 --- a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java @@ -1,22 +1,19 @@ package com.minelittlepony.unicopia; import java.util.Map; -import java.util.Optional; +import java.util.UUID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.ability.magic.CasterView; import com.minelittlepony.unicopia.entity.player.dummy.DummyPlayerEntity; -import com.minelittlepony.unicopia.server.world.Ether; +import com.minelittlepony.unicopia.particle.ParticleSpawner; import com.mojang.authlib.GameProfile; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.PacketByteBuf; -import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; -import net.minecraft.world.BlockView; import net.minecraft.world.World; public class InteractionManager { @@ -37,11 +34,8 @@ public class InteractionManager { return INSTANCE; } - public Optional getCasterView(BlockView view) { - if (view instanceof ServerWorld world) { - return Optional.of(Ether.get(world)); - } - return Optional.empty(); + public ParticleSpawner createBoundParticle(UUID id) { + return ParticleSpawner.EMPTY; } public Map readChapters(PacketByteBuf buf) { @@ -92,4 +86,8 @@ public class InteractionManager { public PlayerEntity createPlayer(World world, GameProfile profile) { return new DummyPlayerEntity(world, profile); } + + public void sendPlayerLookAngles(PlayerEntity player) { + + } } diff --git a/src/main/java/com/minelittlepony/unicopia/Owned.java b/src/main/java/com/minelittlepony/unicopia/Owned.java index 37642a1d..337a59ed 100644 --- a/src/main/java/com/minelittlepony/unicopia/Owned.java +++ b/src/main/java/com/minelittlepony/unicopia/Owned.java @@ -5,6 +5,8 @@ import java.util.UUID; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.item.FriendshipBraceletItem; + import net.minecraft.entity.Entity; /** @@ -27,14 +29,23 @@ public interface Owned { * Since {@link Owned#getMaster()} will only return if the owner is loaded, use this to perform checks * in the owner's absence. */ - default Optional getMasterId() { - return Optional.of(getMaster()).map(Entity::getUuid); + Optional getMasterId(); + + default boolean isOwnerOrFriend(Entity target) { + return FriendshipBraceletItem.isComrade(this, target) || isOwnerOrVehicle(target); + } + + default boolean isOwnerOrVehicle(@Nullable Entity target) { + if (isOwnedBy(target)) { + return true; + } + + Entity owner = getMaster(); + return target != null && owner != null && owner.isConnectedThroughVehicle(target); } default boolean isOwnedBy(@Nullable Object owner) { - return owner instanceof Entity e - && getMasterId().isPresent() - && e.getUuid().equals(getMasterId().get()); + return owner instanceof Entity e && e.getUuid().equals(getMasterId().orElse(null)); } default boolean hasCommonOwner(Owned sibling) { diff --git a/src/main/java/com/minelittlepony/unicopia/USounds.java b/src/main/java/com/minelittlepony/unicopia/USounds.java index 23980e16..3f53eca3 100644 --- a/src/main/java/com/minelittlepony/unicopia/USounds.java +++ b/src/main/java/com/minelittlepony/unicopia/USounds.java @@ -62,11 +62,18 @@ public interface USounds { SoundEvent ENTITY_SOMBRA_AMBIENT = register("entity.sombra.ambient"); SoundEvent ENTITY_SOMBRA_LAUGH = register("entity.sombra.laugh"); SoundEvent ENTITY_SOMBRA_SNICKER = register("entity.sombra.snicker"); - SoundEvent ENTITY_SOMBRA_SCARY = USounds.Vanilla.ENTITY_GHAST_AMBIENT; + SoundEvent ENTITY_SOMBRA_SCARY = ENTITY_GHAST_AMBIENT; SoundEvent ENTITY_CRYSTAL_SHARDS_AMBIENT = BLOCK_AMETHYST_BLOCK_HIT; SoundEvent ENTITY_CRYSTAL_SHARDS_JOSTLE = BLOCK_AMETHYST_BLOCK_BREAK; + SoundEvent ENTITY_IGNIMEOUS_BULB_HURT = ENTITY_WARDEN_HURT; + SoundEvent ENTITY_IGNIMEOUS_BULB_DEATH = ENTITY_WARDEN_DEATH; + + SoundEvent ENTITY_TENTACLE_ROAR = ENTITY_RAVAGER_ROAR; + SoundEvent ENTITY_TENTACLE_AMBIENT = BLOCK_CONDUIT_AMBIENT_SHORT; + SoundEvent ENTITY_TENTACLE_DIG = ENTITY_WARDEN_DIG; + SoundEvent ITEM_AMULET_CHARGING = register("item.amulet.charging"); SoundEvent ITEM_AMULET_RECHARGE = register("item.amulet.recharge"); @@ -90,6 +97,8 @@ public interface USounds { SoundEvent ITEM_STAFF_STRIKE = ENTITY_PLAYER_ATTACK_CRIT; SoundEvent ITEM_MAGIC_STAFF_CHARGE = ENTITY_GUARDIAN_ATTACK; + SoundEvent ITEM_CURING_JOKE_CURE = BLOCK_AMETHYST_BLOCK_BREAK; + SoundEvent ITEM_ROCK_LAND = BLOCK_STONE_HIT; RegistryEntry.Reference ITEM_MUFFIN_BOUNCE = BLOCK_NOTE_BLOCK_BANJO; diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java index 91b4fcae..08da479c 100644 --- a/src/main/java/com/minelittlepony/unicopia/UTags.java +++ b/src/main/java/com/minelittlepony/unicopia/UTags.java @@ -23,6 +23,7 @@ public interface UTags { TagKey SHADES = item("shades"); TagKey CHANGELING_EDIBLE = item("food_types/changeling_edible"); TagKey SPOOKED_MOB_DROPS = item("spooked_mob_drops"); + TagKey HAS_NO_TRAITS = item("has_no_traits"); TagKey IS_DELIVERED_AGGRESSIVELY = item("is_delivered_aggressively"); TagKey FLOATS_ON_CLOUDS = item("floats_on_clouds"); TagKey COOLS_OFF_KIRINS = item("cools_off_kirins"); @@ -42,6 +43,8 @@ public interface UTags { TagKey CRYSTAL_HEART_BASE = block("crystal_heart_base"); TagKey CRYSTAL_HEART_ORNAMENT = block("crystal_heart_ornament"); + TagKey UNAFFECTED_BY_GROW_ABILITY = block("unaffected_by_grow_ability"); + TagKey KICKS_UP_DUST = block("kicks_up_dust"); TagKey POLEARM_MINEABLE = block("mineable/polearm"); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java b/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java index d9a977b4..8a291d0a 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java @@ -209,6 +209,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable { warmup = 0; if (data.isPresent()) { + InteractionManager.instance().sendPlayerLookAngles(player.asEntity()); Channel.CLIENT_PLAYER_ABILITY.sendToServer(new MsgPlayerAbility<>(ability, data, ActivationType.NONE)); } else { player.asEntity().playSound(USounds.GUI_ABILITY_FAIL, 1, 1); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/ChangeFormAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/ChangeFormAbility.java index 4c841b1a..64fabfa4 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/ChangeFormAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/ChangeFormAbility.java @@ -76,9 +76,9 @@ public class ChangeFormAbility implements Ability { targets.forEach(target -> { Race supressed = target.getSuppressedRace(); if (target == player || supressed.isUnset() == isTransforming) { - Race actualRace = target.getSpecies(); + Race actualRace = isTransforming ? target.getSpecies() : Race.UNSET; target.setSpecies(supressed.or(player.getCompositeRace().potential())); - target.setSuppressedRace(isTransforming ? actualRace : Race.UNSET); + target.setSuppressedRace(actualRace); } }); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java index ee82de7d..ec910617 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java @@ -1,22 +1,37 @@ package com.minelittlepony.unicopia.ability; import java.util.Optional; - +import java.util.function.DoubleSupplier; +import java.util.function.Supplier; import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.UTags; 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.item.TransformCropsRecipe; +import com.minelittlepony.unicopia.item.URecipes; import com.minelittlepony.unicopia.particle.MagicParticleEffect; +import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.minelittlepony.unicopia.server.world.BlockDestructionManager; import com.minelittlepony.unicopia.util.TraceHelper; +import com.minelittlepony.unicopia.util.VecHelper; + +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; +import net.minecraft.block.CarrotsBlock; +import net.minecraft.block.FarmlandBlock; import net.minecraft.item.BoneMealItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; +import net.minecraft.particle.ParticleTypes; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import net.minecraft.world.WorldEvents; /** * Earth Pony ability to grow crops @@ -57,10 +72,14 @@ public class EarthPonyGrowAbility implements Ability { public boolean apply(Pony player, Pos data) { int count = 0; - for (BlockPos pos : BlockPos.iterate( - data.pos().add(-2, -2, -2), - data.pos().add( 2, 2, 2))) { - count += applySingle(player.asWorld(), player.asWorld().getBlockState(pos), pos); + if (!applyDirectly(player, data.pos())) { + for (BlockPos pos : BlockPos.iterate( + data.pos().add(-2, -2, -2), + data.pos().add( 2, 2, 2))) { + count += applySingle(player, player.asWorld(), player.asWorld().getBlockState(pos), pos); + } + } else { + count = 1; } if (count > 0) { @@ -69,7 +88,7 @@ public class EarthPonyGrowAbility implements Ability { return true; } - protected int applySingle(World w, BlockState state, BlockPos pos) { + protected int applySingle(Pony player, World w, BlockState state, BlockPos pos) { ItemStack stack = new ItemStack(Items.BONE_MEAL); @@ -77,12 +96,33 @@ public class EarthPonyGrowAbility implements Ability { return growable.grow(w, state, pos) ? 1 : 0; } + if (state.isOf(Blocks.CARROTS)) { + if (state.get(CarrotsBlock.AGE) == CarrotsBlock.MAX_AGE) { + boolean transform = w.random.nextInt(3) == 0; + spawnConversionParticles(w, pos, transform); + if (transform) { + w.setBlockState(pos, UBlocks.GOLD_ROOT.getDefaultState().with(CarrotsBlock.AGE, CarrotsBlock.MAX_AGE)); + } + + return 5; + } + } + + if (w.getBlockState(pos).isIn(UTags.UNAFFECTED_BY_GROW_ABILITY)) { + return 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()); + FarmlandBlock.setToDirt(null, state, w, pos.down()); } w.setBlockState(pos, UBlocks.PLUNDER_VINE_BUD.getDefaultState()); + } else if (w.random.nextInt(5000) == 0) { + if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) { + FarmlandBlock.setToDirt(null, state, w, pos.down()); + } + UBlocks.CURING_JOKE.grow(w, state, pos); } return 1; } @@ -94,6 +134,56 @@ public class EarthPonyGrowAbility implements Ability { return 0; } + private boolean applyDirectly(Pony player, BlockPos pos) { + return player.asWorld().getRecipeManager() + .getAllMatches(URecipes.GROWING, new TransformCropsRecipe.PlacementArea(player, pos), player.asWorld()) + .stream() + .map(recipe -> recipe.value().checkPattern(player.asWorld(), pos)) + .filter(result -> result.matchedLocations().size() + 1 >= TransformCropsRecipe.MINIMUM_INPUT) + .filter(result -> { + boolean transform = result.shoudTransform(player.asWorld().random); + + player.playSound(USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, 1); + + result.matchedLocations().forEach(cell -> { + spawnConversionParticles(player.asWorld(), cell.up(), false); + BlockDestructionManager manager = BlockDestructionManager.of(player.asWorld()); + if (transform) { + if (manager.damageBlock(cell, 8) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) { + player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState()); + player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell))); + } + } else { + if (manager.damageBlock(cell, 4) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) { + player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState()); + player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell))); + } + } + }); + + spawnConversionParticles(player.asWorld(), pos, transform); + if (transform) { + player.asWorld().setBlockState(pos, result.recipe().getResult(player.asWorld(), pos)); + } + + return true; + }) + .findFirst() + .isPresent(); + } + + private static void spawnConversionParticles(World w, BlockPos pos, boolean success) { + DoubleSupplier vecComponentFactory = () -> w.random.nextTriangular(0, 0.5); + Supplier posSupplier = () -> pos.toCenterPos().add(VecHelper.supply(vecComponentFactory)); + + for (int i = 0; i < 25; i++) { + ParticleUtils.spawnParticle(w, new MagicParticleEffect(0xFFFF00), posSupplier.get(), Vec3d.ZERO); + if (success) { + ParticleUtils.spawnParticle(w, ParticleTypes.CLOUD, posSupplier.get(), Vec3d.ZERO); + } + } + } + @Override public void warmUp(Pony player, AbilitySlot slot) { player.getMagicalReserves().getExertion().addPercent(30); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index 1a5c72ef..e81f5a0d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.AwaitTickQueue; import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.entity.Living; @@ -17,6 +18,7 @@ import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.server.world.BlockDestructionManager; import com.minelittlepony.unicopia.util.PosHelper; +import com.minelittlepony.unicopia.util.VecHelper; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -26,6 +28,7 @@ import net.minecraft.entity.LivingEntity; import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.particle.BlockStateParticleEffect; import net.minecraft.registry.tag.BlockTags; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; @@ -33,6 +36,7 @@ import net.minecraft.util.math.BlockBox; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraft.world.WorldEvents; @@ -156,7 +160,10 @@ public class EarthPonyStompAbility implements Ability { spawnEffectAround(player, center, radius, rad); ParticleUtils.spawnParticle(player.getWorld(), UParticles.GROUND_POUND, player.getX(), player.getY() - 1, player.getZ(), 0, 0, 0); - ParticleUtils.spawnParticle(player.getWorld(), UParticles.SHOCKWAVE, player.getX(), player.getY() - 1, player.getZ(), 0, 0, 0); + BlockState steppingState = player.getSteppingBlockState(); + if (steppingState.isIn(UTags.KICKS_UP_DUST)) { + ParticleUtils.spawnParticle(player.getWorld(), new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), player.getBlockPos().down().toCenterPos(), Vec3d.ZERO); + } iplayer.subtractEnergyCost(rad); iplayer.asEntity().addExhaustion(3); @@ -218,6 +225,12 @@ public class EarthPonyStompAbility implements Ability { } else { w.syncWorldEvent(WorldEvents.BLOCK_BROKEN, pos, Block.getRawIdFromState(state)); } + + if (state.isIn(UTags.KICKS_UP_DUST)) { + if (w.random.nextInt(4) == 0 && w.isAir(pos.up()) && w.getFluidState(pos.up()).isEmpty()) { + ParticleUtils.spawnParticle(w, new BlockStateParticleEffect(UParticles.DUST_CLOUD, state), pos.up().toCenterPos(), VecHelper.supply(() -> w.random.nextTriangular(0, 0.1F))); + } + } } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java index 087e1cb0..bbe7256e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java @@ -10,10 +10,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.MagicParticleEffect; -import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; -import com.minelittlepony.unicopia.particle.UParticles; - -import net.minecraft.util.math.Vec3d; /** * Pegasus ability to perform rainbooms @@ -72,7 +68,6 @@ public class PegasusRainboomAbility implements Ability { } if (player.consumeSuperMove()) { - player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO); SpellType.RAINBOOM.withTraits().apply(player, CastingMethod.INNATE); } return true; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java index 99dda711..d965f9da 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java @@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.entity.player.Pony; @@ -92,7 +93,11 @@ public class UnicornDispellAbility implements Ability { public boolean apply(Pony player, Pos data) { player.setAnimation(Animation.WOLOLO, Animation.Recipient.ANYONE); Caster.stream(VecHelper.findInRange(player.asEntity(), player.asWorld(), data.vec(), 3, EquinePredicates.IS_PLACED_SPELL).stream()).forEach(target -> { - target.getSpellSlot().clear(); + target.getSpellSlot().forEach(spell -> { + spell.setDead(); + spell.tickDying(target); + return Operation.ofBoolean(!spell.isDead()); + }, true); }); return true; } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java index 007da627..7cdfdaef 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java @@ -19,4 +19,8 @@ public interface Affine { default boolean isFriendlyTogether(Affine other) { return getAffinity() != Affinity.BAD && other.getAffinity() != Affinity.BAD; } + + default boolean applyInversion(Affine other, boolean friendly) { + return isEnemy(other) ? !friendly : friendly; + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/Caster.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/Caster.java index 98264836..150b1004 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/Caster.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/Caster.java @@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.damage.UDamageSources; import com.minelittlepony.unicopia.particle.ParticleSource; +import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.server.world.ModificationType; import com.minelittlepony.unicopia.util.SoundEmitter; import com.minelittlepony.unicopia.util.VecHelper; @@ -99,11 +100,7 @@ public interface Caster extends } default boolean canCastAt(Vec3d pos) { - return findAllSpellsInRange(500, SpellType.ARCANE_PROTECTION::isOn).noneMatch(caster -> caster - .getSpellSlot().get(SpellType.ARCANE_PROTECTION, false) - .filter(spell -> spell.blocksMagicFor(caster, this, pos)) - .isPresent() - ); + return !Ether.get(asWorld()).anyMatch(SpellType.ARCANE_PROTECTION, (spell, caster) -> spell.blocksMagicFor(caster, this, pos)); } static Stream> stream(Stream entities) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/CasterView.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/CasterView.java deleted file mode 100644 index 572f4542..00000000 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/CasterView.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.minelittlepony.unicopia.ability.magic; - -import java.util.Map; -import java.util.function.Predicate; -import java.util.stream.Stream; - -import org.jetbrains.annotations.Nullable; - -import com.minelittlepony.unicopia.EquinePredicates; -import com.minelittlepony.unicopia.ability.magic.spell.Spell; -import com.minelittlepony.unicopia.util.VecHelper; - -import net.minecraft.entity.Entity; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.*; - -public interface CasterView { - EntityView getWorld(); - - default Stream, S>> findAllSpellsInRange(BlockPos pos, double radius, SpellPredicate type) { - return findAllCastersInRange(pos, radius).flatMap(caster -> { - return caster.getSpellSlot().stream(type, false).map(spell -> { - return Map.entry(caster, spell); - }); - }); - } - - default Stream> findAllCastersInRange(BlockPos pos, double radius) { - return findAllCastersInRange(pos, radius, null); - } - - default Stream> findAllCastersInRange(BlockPos pos, double radius, @Nullable Predicate test) { - return Caster.stream(findAllEntitiesInRange(pos, radius, test == null ? EquinePredicates.IS_CASTER : EquinePredicates.IS_CASTER.and(test))); - } - - default Stream findAllEntitiesInRange(BlockPos pos, double radius, @Nullable Predicate test) { - return VecHelper.findInRange(null, getWorld(), Vec3d.ofCenter(pos), radius, test).stream(); - } - - default Stream findAllEntitiesInRange(BlockPos pos, double radius) { - return findAllEntitiesInRange(pos, radius, null); - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java index bbe1ad70..e703911d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java @@ -39,6 +39,13 @@ public interface SpellContainer { */ void put(@Nullable Spell effect); + /** + * Cleanly removes a spell from this spell container. + * + * @param spellid ID of the spell to remove. + */ + void remove(UUID spellid); + /** * Removes all active effects that match or contain a matching effect. * diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java index f6219d62..68a006ed 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java @@ -69,11 +69,20 @@ public abstract class AbstractDelegatingSpell implements Spell, getDelegates().forEach(Spell::setDead); } + @Override + public void tickDying(Caster caster) { + } + @Override public boolean isDead() { return getDelegates().isEmpty() || getDelegates().stream().allMatch(Spell::isDead); } + @Override + public boolean isDying() { + return false; + } + @Override public boolean isDirty() { return dirty || getDelegates().stream().anyMatch(Spell::isDirty); @@ -110,7 +119,13 @@ public abstract class AbstractDelegatingSpell implements Spell, @Override public boolean tick(Caster source, Situation situation) { - return execute(getDelegates().stream(), a -> a.tick(source, situation)); + return execute(getDelegates().stream(), a -> { + if (a.isDying()) { + a.tickDying(source); + return !a.isDead(); + } + return a.tick(source, situation); + }); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index 7154056a..ed03506d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -13,10 +13,6 @@ import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.network.Channel; import com.minelittlepony.unicopia.network.MsgCasterLookRequest; -import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; -import com.minelittlepony.unicopia.particle.ParticleHandle; -import com.minelittlepony.unicopia.particle.UParticles; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.NbtSerialisable; @@ -24,6 +20,7 @@ import net.minecraft.nbt.*; import net.minecraft.registry.*; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; @@ -41,9 +38,10 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS private RegistryKey dimension; /** - * The visual effect + * ID of the placed counterpart of this spell. */ - private final ParticleHandle particlEffect = new ParticleHandle(); + @Nullable + private UUID placedSpellId; /** * The cast spell entity @@ -58,6 +56,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS public float pitch; public float yaw; + private int prevAge; + private int age; + + private boolean dead; + private int prevDeathTicks; + private int deathTicks; + private Optional position = Optional.empty(); public PlaceableSpell(CustomisedSpellType type) { @@ -69,23 +74,42 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS return this; } + public float getAge(float tickDelta) { + return MathHelper.lerp(tickDelta, prevAge, age); + } + + public float getScale(float tickDelta) { + float add = MathHelper.clamp(getAge(tickDelta) / 25F, 0, 1); + float subtract = dead ? 1 - (MathHelper.lerp(tickDelta, prevDeathTicks, deathTicks) / 20F) : 0; + return MathHelper.clamp(add - subtract, 0, 1); + } + + @Override + public boolean isDying() { + return dead && deathTicks > 0; + } + + @Override + public void setDead() { + super.setDead(); + dead = true; + deathTicks = 20; + } + + @Override + public boolean isDead() { + return dead && deathTicks <= 0; + } + @Override public Collection getDelegates() { return List.of(spell); } - @Override - public void setDead() { - super.setDead(); - particlEffect.destroy(); - } - @Override public boolean tick(Caster source, Situation situation) { - if (situation == Situation.BODY) { if (!source.isClient()) { - if (dimension == null) { dimension = source.asWorld().getRegistryKey(); if (source instanceof Pony) { @@ -105,29 +129,31 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS if (situation == Situation.GROUND_ENTITY) { if (!source.isClient()) { - Ether ether = Ether.get(source.asWorld()); - if (ether.getEntry(getType(), source).isEmpty()) { + if (Ether.get(source.asWorld()).get(this, source) == null) { setDead(); return false; } } - if (spell instanceof PlacementDelegate delegate) { - delegate.updatePlacement(source, this); + prevAge = age; + if (age < 25) { + age++; } - getParticleEffectAttachment(source).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_COLOR, spell.getType().getColor()); - }); - return super.tick(source, Situation.GROUND); } return !isDead(); } + @Override + public void tickDying(Caster caster) { + prevDeathTicks = deathTicks; + deathTicks--; + } + private void checkDetachment(Caster source, EntityValues target) { - if (getWorld(source).map(Ether::get).flatMap(ether -> ether.getEntry(getType(), target.uuid())).isEmpty()) { + if (getWorld(source).map(Ether::get).map(ether -> ether.get(getType(), target, placedSpellId)).isEmpty()) { setDead(); } } @@ -143,7 +169,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS entity.getSpellSlot().put(copy); entity.setCaster(source); entity.getWorld().spawnEntity(entity); - Ether.get(entity.getWorld()).put(getType(), entity); + placedSpellId = copy.getUuid(); + Ether.get(entity.getWorld()).getOrCreate(copy, entity); castEntity.set(entity); setDirty(); @@ -174,8 +201,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS if (!source.isClient()) { castEntity.getTarget().ifPresent(target -> { getWorld(source).map(Ether::get) - .flatMap(ether -> ether.getEntry(getType(), target.uuid())) - .ifPresent(Ether.Entry::markDead); + .ifPresent(ether -> ether.remove(getType(), target.uuid())); }); castEntity.set(null); getSpellEntity(source).ifPresent(e -> { @@ -183,7 +209,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS }); if (source.asEntity() instanceof CastSpellEntity spellcast) { - Ether.get(source.asWorld()).remove(getType(), source); + Ether.get(source.asWorld()).remove(this, source); } } super.onDestroyed(source); @@ -197,12 +223,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS return castEntity.getTarget().map(EntityValues::pos); } - public Optional getParticleEffectAttachment(Caster source) { - return particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, pitch + 90, yaw), Vec3d.ZERO, Vec3d.ZERO); - }); - } - protected Optional getWorld(Caster source) { return Optional.ofNullable(dimension) .map(dim -> source.asWorld().getServer().getWorld(dim)); @@ -211,11 +231,17 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); + compound.putBoolean("dead", dead); + compound.putInt("deathTicks", deathTicks); + compound.putInt("age", age); compound.putFloat("pitch", pitch); compound.putFloat("yaw", yaw); position.ifPresent(pos -> { compound.put("position", NbtSerialisable.writeVector(pos)); }); + if (placedSpellId != null) { + compound.putUuid("placedSpellId", placedSpellId); + } if (dimension != null) { compound.putString("dimension", dimension.getValue().toString()); } @@ -226,9 +252,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); + dead = compound.getBoolean("dead"); + deathTicks = compound.getInt("deathTicks"); + age = compound.getInt("age"); pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); position = compound.contains("position") ? Optional.of(NbtSerialisable.readVector(compound.getList("position", NbtElement.FLOAT_TYPE))) : Optional.empty(); + placedSpellId = compound.containsUuid("placedSpellId") ? compound.getUuid("placedSpellId") : null; if (compound.contains("dimension", NbtElement.STRING_TYPE)) { Identifier id = Identifier.tryParse(compound.getString("dimension")); if (id != null) { @@ -257,9 +287,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS } public interface PlacementDelegate { - void onPlaced(Caster source, PlaceableSpell parent, CastSpellEntity entity); - - void updatePlacement(Caster source, PlaceableSpell parent); } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java index 4bf0d726..6c6d1099 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java @@ -1,12 +1,16 @@ package com.minelittlepony.unicopia.ability.magic.spell; +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.*; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.player.Pony; -import com.minelittlepony.unicopia.particle.ParticleHandle; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; +import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; +import com.minelittlepony.unicopia.particle.ParticleSpawner; +import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect; import com.minelittlepony.unicopia.server.world.ModificationType; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.util.shape.Shape; @@ -26,7 +30,8 @@ public class RainboomAbilitySpell extends AbstractSpell { private static final int RADIUS = 5; private static final Shape EFFECT_RANGE = new Sphere(false, RADIUS); - private final ParticleHandle particlEffect = new ParticleHandle(); + @Nullable + private ParticleSpawner boundParticle; private int age; @@ -35,11 +40,6 @@ public class RainboomAbilitySpell extends AbstractSpell { setHidden(true); } - @Override - protected void onDestroyed(Caster source) { - particlEffect.destroy(); - } - @Override public boolean tick(Caster source, Situation situation) { @@ -47,14 +47,15 @@ public class RainboomAbilitySpell extends AbstractSpell { return false; } - particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(UParticles.RAINBOOM_TRAIL, source.getOriginVector(), Vec3d.ZERO); - }).ifPresent(attachment -> { - attachment.setAttribute(Attachment.ATTR_BOUND, 1); - }); - if (source.isClient()) { - // source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO); + if (boundParticle == null) { + boundParticle = InteractionManager.INSTANCE.createBoundParticle(getUuid()); + } + boundParticle.addParticle(new TargetBoundParticleEffect(UParticles.RAINBOOM_TRAIL, source.asEntity()), source.getOriginVector(), Vec3d.ZERO); + + if (age == 0) { + source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO); + } } source.findAllEntitiesInRange(RADIUS).forEach(e -> { @@ -92,5 +93,6 @@ public class RainboomAbilitySpell extends AbstractSpell { public void fromNBT(NbtCompound compound) { super.fromNBT(compound); age = compound.getInt("age"); + boundParticle = null; } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java index d6bb1ccf..55a5f3bf 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java @@ -61,6 +61,8 @@ public interface Spell extends NbtSerialisable, Affine { */ boolean isDead(); + boolean isDying(); + /** * Returns true if this effect has changes that need to be sent to the client. */ @@ -68,6 +70,7 @@ public interface Spell extends NbtSerialisable, Affine { /** * Applies this spell to the supplied caster. + * @param caster The caster to apply the spell to */ default boolean apply(Caster caster) { caster.getSpellSlot().put(this); @@ -76,7 +79,7 @@ public interface Spell extends NbtSerialisable, Affine { /** * Gets the default form of this spell used to apply to a caster. - * @param caster + * @param caster The caster currently fueling this spell */ default Spell prepareForCast(Caster caster, CastingMethod method) { return this; @@ -89,6 +92,12 @@ public interface Spell extends NbtSerialisable, Affine { */ boolean tick(Caster caster, Situation situation); + /** + * Called on spells that are actively dying to update any post-death animations before removal. + * @param caster The caster currently fueling this spell + */ + void tickDying(Caster caster); + /** * Marks this effect as dirty. */ diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimedSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimedSpell.java index 9e19b8ae..d81e4f39 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimedSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimedSpell.java @@ -29,7 +29,7 @@ public interface TimedSpell extends Spell { } public float getPercentTimeRemaining(float tickDelta) { - return MathHelper.lerp(tickDelta, prevDuration, duration) / maxDuration; + return MathHelper.lerp(tickDelta, prevDuration, duration) / (float)maxDuration; } public int getTicksRemaining() { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/AltarRecipeMatch.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/AltarRecipeMatch.java new file mode 100644 index 00000000..820f7667 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/AltarRecipeMatch.java @@ -0,0 +1,42 @@ +package com.minelittlepony.unicopia.ability.magic.spell.crafting; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.item.UItems; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; + +public record AltarRecipeMatch( + ItemEntity target, + List ingredients, + ItemStack result + ) { + + @Nullable + public static AltarRecipeMatch of(List inputs) { + ItemEntity clock = inputs.stream().filter(item -> item.getStack().isOf(Items.CLOCK)).findFirst().orElse(null); + + if (clock != null) { + return new AltarRecipeMatch(clock, List.of(), UItems.SPECTRAL_CLOCK.getDefaultStack()); + } + + return null; + } + + public boolean isRemoved() { + return target.isRemoved() || ingredients.stream().anyMatch(ItemEntity::isRemoved); + } + + public void craft() { + ItemStack clockStack = result.copyWithCount(target.getStack().getCount()); + clockStack.setNbt(target.getStack().getNbt()); + target.setStack(clockStack); + target.setInvulnerable(true); + ingredients.forEach(Entity::discard); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java index a8c1b706..8b64d9c3 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java @@ -12,6 +12,7 @@ import net.minecraft.nbt.NbtCompound; public abstract class AbstractSpell implements Spell { private boolean dead; + private boolean dying; private boolean dirty; private boolean hidden; private boolean destroyed; @@ -45,7 +46,7 @@ public abstract class AbstractSpell implements Spell { @Override public final void setDead() { - dead = true; + dying = true; setDirty(); } @@ -54,6 +55,11 @@ public abstract class AbstractSpell implements Spell { return dead; } + @Override + public final boolean isDying() { + return dying; + } + @Override public final boolean isDirty() { return dirty; @@ -82,6 +88,11 @@ public abstract class AbstractSpell implements Spell { protected void onDestroyed(Caster caster) { } + @Override + public void tickDying(Caster caster) { + dead = true; + } + @Override public final void destroy(Caster caster) { if (destroyed) { @@ -94,6 +105,7 @@ public abstract class AbstractSpell implements Spell { @Override public void toNBT(NbtCompound compound) { + compound.putBoolean("dying", dying); compound.putBoolean("dead", dead); compound.putBoolean("hidden", hidden); compound.putUuid("uuid", uuid); @@ -106,6 +118,7 @@ public abstract class AbstractSpell implements Spell { if (compound.contains("uuid")) { uuid = compound.getUuid("uuid"); } + dying = compound.getBoolean("dying"); dead = compound.getBoolean("dead"); hidden = compound.getBoolean("hidden"); if (compound.contains("traits")) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AreaProtectionSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AreaProtectionSpell.java index 75615785..920c5bd5 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AreaProtectionSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AreaProtectionSpell.java @@ -9,6 +9,7 @@ import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import com.minelittlepony.unicopia.particle.MagicParticleEffect; +import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.entity.Entity; @@ -42,6 +43,8 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell { source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO); } }); + } else { + Ether.get(source.asWorld()).getOrCreate(this, source); } source.findAllSpellsInRange(radius, e -> isValidTarget(source, e)).filter(caster -> !caster.hasCommonOwner(source)).forEach(caster -> { @@ -51,6 +54,11 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell { return !isDead(); } + @Override + protected void onDestroyed(Caster caster) { + Ether.get(caster.asWorld()).remove(this, caster); + } + /** * Calculates the maximum radius of the shield. aka The area of effect. */ diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java index 669411a1..9af5cddb 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java @@ -7,7 +7,6 @@ import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.particle.FollowingParticleEffect; -import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; @@ -16,6 +15,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.entity.Entity; import net.minecraft.entity.ItemEntity; import net.minecraft.nbt.NbtCompound; +import net.minecraft.particle.ParticleTypes; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -44,9 +44,10 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp if (timer.getTicksRemaining() <= 0) { return false; } + + setDirty(); } - setDirty(); target.getOrEmpty(caster.asWorld()) .filter(entity -> entity.distanceTo(caster.asEntity()) > getDrawDropOffRange(caster)) .ifPresent(entity -> { @@ -59,12 +60,13 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp @Override public void generateParticles(Caster source) { - double range = getDrawDropOffRange(source) + 10; + double range = getDrawDropOffRange(source); + Vec3d origin = getOrigin(source); - source.spawnParticles(getOrigin(source), new Sphere(false, range), 7, p -> { + source.spawnParticles(origin, new Sphere(false, range), 7, p -> { source.addParticle( - new FollowingParticleEffect(UParticles.HEALTH_DRAIN, source.asEntity(), 0.4F) - .withChild(new MagicParticleEffect(getType().getColor())), + new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F) + .withChild(ParticleTypes.AMBIENT_ENTITY_EFFECT), p, Vec3d.ZERO ); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java index c2b837b0..63afe303 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java @@ -11,12 +11,10 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; import com.minelittlepony.unicopia.entity.player.Pony; -import com.minelittlepony.unicopia.particle.ParticleHandle; -import com.minelittlepony.unicopia.particle.SphereParticleEffect; import com.minelittlepony.unicopia.particle.UParticles; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; +import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; @@ -48,11 +46,11 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, .with(Trait.POWER, 1) .build(); - protected final ParticleHandle particlEffect = new ParticleHandle(); - private final Timer timer; private int struggles; + + private float prevRadius; private float radius; protected BubbleSpell(CustomisedSpellType type) { @@ -66,6 +64,10 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, return timer; } + public float getRadius(float tickDelta) { + return MathHelper.lerp(tickDelta, prevRadius, radius); + } + @Override public boolean apply(Caster source) { @@ -95,14 +97,19 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, public boolean tick(Caster source, Situation situation) { if (situation == Situation.PROJECTILE) { - - source.spawnParticles(ParticleTypes.BUBBLE, 2); + source.spawnParticles(UParticles.BUBBLE, 2); return true; } timer.tick(); - if (timer.getTicksRemaining() <= 0) { + boolean done = timer.getTicksRemaining() <= 0; + + source.spawnParticles(source.getOriginVector().add(0, 1, 0), new Sphere(true, radius * (done ? 0.25F : 0.5F)), done ? 13 : 1, pos -> { + source.addParticle(done ? ParticleTypes.BUBBLE_POP : UParticles.BUBBLE, pos, Vec3d.ZERO); + }); + + if (done) { return false; } @@ -116,7 +123,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, source.asEntity().fallDistance = 0; - Vec3d origin = source.getOriginVector(); + prevRadius = radius; if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) { setDirty(); @@ -128,18 +135,11 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, } } - particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0xFFFFFF, 0.3F, 0, new Vec3d(0, radius / 2F, 0)), origin, Vec3d.ZERO); - }).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_RADIUS, radius); - }); - return !isDead(); } @Override protected void onDestroyed(Caster source) { - particlEffect.destroy(); if (source.asEntity() instanceof LivingEntity l) { MODIFIERS.forEach((attribute, modifier) -> { if (l.getAttributes().hasAttribute(attribute)) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index bda10118..02eb96d3 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -10,11 +10,11 @@ import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; +import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; +import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; -import com.minelittlepony.unicopia.particle.SphereParticleEffect; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; @@ -37,10 +37,6 @@ import net.minecraft.world.World.ExplosionSourceType; /** * More powerful version of the vortex spell which creates a black hole. - * - * TODO: Possible uses - * - Garbage bin - * - Link with a teleportation spell to create a wormhole */ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelegate.BlockHitListener { public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder() @@ -52,7 +48,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega private static final Vec3d SPHERE_OFFSET = new Vec3d(0, 2, 0); - private int age = 0; private float accumulatedMass = 0; protected DarkVortexSpell(CustomisedSpellType type) { @@ -84,17 +79,10 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega return true; } - age++; - setDirty(); - - if (age % 20 == 0) { + if (source.asEntity().age % 20 == 0) { source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1); } - if (!source.subtractEnergyCost(-accumulatedMass)) { - setDead(); - } - if (!source.isClient() && source.asWorld().random.nextInt(300) == 0) { ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, getOrigin(source), Vec3d.ZERO); } @@ -102,6 +90,13 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega return super.tick(source, situation); } + @Override + protected void consumeManage(Caster source, long costMultiplier, float knowledge) { + if (!source.subtractEnergyCost(-accumulatedMass)) { + setDead(); + } + } + @Override public boolean isFriendlyTogether(Affine other) { return accumulatedMass < 4; @@ -116,21 +111,20 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega public void generateParticles(Caster source) { super.generateParticles(source); - float radius = (float)getEventHorizonRadius(); - - particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0x000000, 0.99F, radius, SPHERE_OFFSET), source.getOriginVector(), Vec3d.ZERO); - }).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_RADIUS, radius); - p.setAttribute(Attachment.ATTR_OPACITY, 2F); - }); - particlEffect.update(getUuid(), "_ring", source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.DISK, 0xAAAAAA, 0.4F, radius + 1, SPHERE_OFFSET), getOrigin(source), Vec3d.ZERO); - }).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_RADIUS, radius * 0F); - }); - - source.spawnParticles(ParticleTypes.SMOKE, 3); + if (getEventHorizonRadius() > 0.3) { + double range = getDrawDropOffRange(source); + Vec3d origin = getOrigin(source); + source.spawnParticles(origin, new Sphere(false, range), 1, p -> { + if (!source.asWorld().isAir(BlockPos.ofFloored(p))) { + source.addParticle( + new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F) + .withChild(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE), + p, + Vec3d.ZERO + ); + } + }); + } } @Override @@ -162,7 +156,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega applyRadialEffect(source, e, e.getPos().distanceTo(origin), radius); }); } - setDirty(); }); } } @@ -180,8 +173,8 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega // 2. max force (at dist 0) is taken from accumulated mass // 3. force reaches 0 at distance of drawDropOffRange - private double getEventHorizonRadius() { - return Math.sqrt(Math.max(0.001, getMass() - 12)); + public double getEventHorizonRadius() { + return Math.sqrt(Math.max(0.001, getMass() / 3F)); } private double getAttractiveForce(Caster source, Entity target) { @@ -189,8 +182,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega } private double getMass() { - float pulse = (float)Math.sin(age * 8) / 1F; - return 10 + Math.min(15, Math.min(0.5F + pulse, (float)Math.exp(age) / 8F - 90) + accumulatedMass / 10F) + pulse; + return Math.min(15, 0.1F + accumulatedMass / 10F); } @Override @@ -202,6 +194,11 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega if (distance <= getEventHorizonRadius() + 0.5) { target.setVelocity(target.getVelocity().multiply(distance / (2 * radius))); + if (distance < 1) { + target.setVelocity(target.getVelocity().multiply(distance)); + + } + Living.updateVelocity(target); @Nullable Entity master = source.getMaster(); @@ -221,13 +218,19 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega double massOfTarget = AttractionUtils.getMass(target); - accumulatedMass += massOfTarget; - setDirty(); + if (!source.isClient() && massOfTarget != 0) { + accumulatedMass += massOfTarget; + setDirty(); + } + target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE); if (!(target instanceof PlayerEntity)) { target.discard(); source.asWorld().playSound(null, source.getOrigin(), USounds.ENCHANTMENT_CONSUMPTION_CONSUME, SoundCategory.AMBIENT, 2, 0.02F); } + if (target.isAlive()) { + target.damage(source.asEntity().getDamageSources().outOfWorld(), Integer.MAX_VALUE); + } source.subtractEnergyCost(-massOfTarget * 10); source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.02F); @@ -243,14 +246,12 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); - compound.putInt("age", age); compound.putFloat("accumulatedMass", accumulatedMass); } @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); - age = compound.getInt("age"); accumulatedMass = compound.getFloat("accumulatedMass"); } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisperseIllusionSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisperseIllusionSpell.java index 4806d10d..9affae3e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisperseIllusionSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisperseIllusionSpell.java @@ -12,7 +12,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.util.math.Vec3d; /** - * An area-effect spell that disperses illussions. + * An area-effect spell that disperses illusions. */ public class DisperseIllusionSpell extends AbstractAreaEffectSpell { protected DisperseIllusionSpell(CustomisedSpellType type) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisplacementSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisplacementSpell.java index 14d3abf8..a58b77de 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisplacementSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisplacementSpell.java @@ -6,8 +6,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.*; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; -import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; @@ -16,7 +14,7 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.math.Vec3d; -public class DisplacementSpell extends AbstractSpell implements HomingSpell, PlaceableSpell.PlacementDelegate, ProjectileDelegate.EntityHitListener { +public class DisplacementSpell extends AbstractSpell implements HomingSpell, ProjectileDelegate.EntityHitListener { private final EntityReference target = new EntityReference<>(); @@ -67,19 +65,6 @@ public class DisplacementSpell extends AbstractSpell implements HomingSpell, Pla originator.subtractEnergyCost(destinationPos.distanceTo(sourcePos) / 20F); } - @Override - public void onPlaced(Caster source, PlaceableSpell parent, CastSpellEntity entity) { - - } - - @Override - public void updatePlacement(Caster caster, PlaceableSpell parent) { - parent.getParticleEffectAttachment(caster).ifPresent(attachment -> { - float r = 3 - (1 - ((ticks + 10) / 20F)) * 3; - attachment.setAttribute(Attachment.ATTR_RADIUS, r); - }); - } - private void teleport(Caster source, Entity entity, Vec3d pos, Vec3d vel) { entity.teleport(pos.x, pos.y, pos.z); entity.setVelocity(vel); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java index c2324a5f..a76ef43b 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java @@ -48,7 +48,7 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell, if (caster instanceof MagicProjectileEntity && getTraits().get(Trait.FOCUS) >= 50) { caster.findAllEntitiesInRange( getTraits().get(Trait.FOCUS) - 49, - EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.notOwnerOrFriend(this, caster)) + EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.validTarget(this, caster)) ).findFirst().ifPresent(target -> { ((MagicProjectileEntity)caster).setHomingTarget(target); }); @@ -60,7 +60,7 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell, if (getTraits().get(Trait.FOCUS) >= 50 && target.getOrEmpty(caster.asWorld()).isEmpty()) { target.set(caster.findAllEntitiesInRange( getTraits().get(Trait.FOCUS) - 49, - EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.notOwnerOrFriend(this, caster)) + EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.validTarget(this, caster)) ).findFirst().orElse(null)); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/HydrophobicSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/HydrophobicSpell.java index 5ff51120..d88d9dd0 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/HydrophobicSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/HydrophobicSpell.java @@ -5,14 +5,16 @@ import java.util.Set; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Caster; -import com.minelittlepony.unicopia.ability.magic.CasterView; +import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; import com.minelittlepony.unicopia.ability.magic.spell.Situation; +import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; +import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.shape.*; @@ -21,8 +23,10 @@ import net.minecraft.fluid.*; import net.minecraft.nbt.*; import net.minecraft.state.property.Properties; import net.minecraft.registry.tag.TagKey; +import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.BlockView; import net.minecraft.world.World; public class HydrophobicSpell extends AbstractSpell { @@ -41,16 +45,15 @@ public class HydrophobicSpell extends AbstractSpell { } @Override - public boolean apply(Caster source) { - if (getTraits().get(Trait.GENEROSITY) > 0) { - return toPlaceable().apply(source); + public Spell prepareForCast(Caster caster, CastingMethod method) { + if ((method == CastingMethod.DIRECT || method == CastingMethod.STAFF) && getTraits().get(Trait.GENEROSITY) > 0) { + return toPlaceable(); } - return super.apply(source); + return this; } @Override public boolean tick(Caster source, Situation situation) { - if (!source.isClient()) { World world = source.asWorld(); @@ -86,7 +89,11 @@ public class HydrophobicSpell extends AbstractSpell { setDead(); } - source.spawnParticles(new Sphere(true, getRange(source)), 10, pos -> { + double range = getRange(source); + var entry = Ether.get(source.asWorld()).getOrCreate(this, source); + entry.radius = (float)range; + + source.spawnParticles(new Sphere(true, range), 10, pos -> { BlockPos bp = BlockPos.ofFloored(pos); if (source.asWorld().getFluidState(bp.up()).isIn(affectedFluid)) { source.addParticle(UParticles.RAIN_DROPS, pos, Vec3d.ZERO); @@ -107,6 +114,7 @@ public class HydrophobicSpell extends AbstractSpell { @Override protected void onDestroyed(Caster caster) { + Ether.get(caster.asWorld()).remove(this, caster); storedFluidPositions.removeIf(entry -> { entry.restore(caster.asWorld()); return true; @@ -162,13 +170,20 @@ public class HydrophobicSpell extends AbstractSpell { } } - public boolean blocksFlow(Caster caster, BlockPos pos, FluidState fluid) { - return fluid.isIn(affectedFluid) && pos.isWithinDistance(caster.getOrigin(), getRange(caster) + 1); + public boolean blocksFlow(Ether.Entry entry, Vec3d center, BlockPos pos, FluidState fluid) { + return fluid.isIn(affectedFluid) && pos.isWithinDistance(center, (double)entry.radius + 1); } - public static boolean blocksFluidFlow(CasterView world, BlockPos pos, FluidState state) { - return world.findAllSpellsInRange(pos, 500, SpellType.HYDROPHOBIC).anyMatch(pair -> { - return pair.getValue().blocksFlow(pair.getKey(), pos, state); - }); + public static boolean blocksFluidFlow(BlockView world, BlockPos pos, FluidState state) { + if (world instanceof ServerWorld sw) { + return Ether.get(sw).anyMatch(SpellType.HYDROPHOBIC, entry -> { + var spell = entry.getSpell(); + var target = entry.entity.getTarget().orElse(null); + return spell != null && target != null && spell.blocksFlow(entry, target.pos(), pos, state); + }); + } + + return false; + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index 680a0ff1..82a66ad9 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -1,6 +1,9 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect; import java.util.Optional; +import java.util.UUID; + +import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; @@ -9,9 +12,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.*; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.EntityReference; +import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; import com.minelittlepony.unicopia.particle.*; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.shape.*; @@ -20,8 +23,10 @@ import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.nbt.NbtCompound; -import net.minecraft.particle.ParticleEffect; +import net.minecraft.network.packet.s2c.play.PositionFlag; import net.minecraft.particle.ParticleTypes; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.WorldEvents; @@ -34,12 +39,14 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme .build(); private static final Shape PARTICLE_AREA = new Sphere(true, 2, 1, 1, 0); + @Nullable + private UUID targetPortalId; + private float targetPortalPitch; + private float targetPortalYaw; private final EntityReference teleportationTarget = new EntityReference<>(); private boolean publishedPosition; - private final ParticleHandle particleEffect = new ParticleHandle(); - private float pitch; private float yaw; @@ -49,6 +56,39 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme super(type); } + public boolean isLinked() { + return teleportationTarget.getTarget().isPresent(); + } + + public Optional> getTarget() { + return teleportationTarget.getTarget(); + } + + public float getPitch() { + return pitch; + } + + public float getYaw() { + return yaw; + } + + public float getTargetPitch() { + return targetPortalPitch; + } + + public float getTargetYaw() { + return targetPortalYaw; + } + + public float getYawDifference() { + return MathHelper.wrapDegrees(180 + targetPortalYaw - yaw); + } + + @SuppressWarnings("unchecked") + private Optional> getTarget(Caster source) { + return getTarget().map(target -> Ether.get(source.asWorld()).get((SpellType)getType(), target, targetPortalId)); + } + @Override public boolean apply(Caster caster) { setOrientation(caster.asEntity().getPitch(), caster.asEntity().getYaw()); @@ -60,29 +100,12 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme if (situation == Situation.GROUND) { if (source.isClient()) { - Vec3d origin = source.getOriginVector(); - - ParticleEffect effect = teleportationTarget.getTarget() - .map(target -> { - getType(); - return (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK); - }) - .orElse(ParticleTypes.ELECTRIC_SPARK); - - source.spawnParticles(origin, particleArea, 5, pos -> { - source.addParticle(effect, pos, Vec3d.ZERO); - }); - - teleportationTarget.getTarget().ifPresentOrElse(target -> { - particleEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.DISK, getType().getColor(), 0.8F, 1.8F, new Vec3d(-pitch + 90, -yaw, 0)), source.getOriginVector(), Vec3d.ZERO); - }); - }, () -> { - particleEffect.destroy(); + source.spawnParticles(particleArea, 5, pos -> { + source.addParticle(ParticleTypes.ELECTRIC_SPARK, pos, Vec3d.ZERO); }); } else { - teleportationTarget.getTarget().ifPresent(target -> { - if (Ether.get(source.asWorld()).getEntry(getType(), target.uuid()).isEmpty()) { + getTarget().ifPresent(target -> { + if (Ether.get(source.asWorld()).get(getType(), target, targetPortalId) == null) { Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid()); teleportationTarget.set(null); setDirty(); @@ -96,37 +119,56 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme ); } - if (!publishedPosition) { - publishedPosition = true; - Ether.Entry entry = Ether.get(source.asWorld()).put(getType(), source); - entry.pitch = pitch; - entry.yaw = yaw; - } + var entry = Ether.get(source.asWorld()).getOrCreate(this, source); + entry.pitch = pitch; + entry.yaw = yaw; + Ether.get(source.asWorld()).markDirty(); } return !isDead(); } - private void tickWithTargetLink(Caster source, Ether.Entry destination) { + private void tickWithTargetLink(Caster source, Ether.Entry destination) { + + if (!MathHelper.approximatelyEquals(targetPortalPitch, destination.pitch)) { + targetPortalPitch = destination.pitch; + setDirty(); + } + if (!MathHelper.approximatelyEquals(targetPortalYaw, destination.yaw)) { + targetPortalYaw = destination.yaw; + setDirty(); + } destination.entity.getTarget().ifPresent(target -> { source.findAllEntitiesInRange(1).forEach(entity -> { - if (!entity.hasPortalCooldown() && entity.timeUntilRegen <= 0) { + if (!entity.hasPortalCooldown()) { + + float approachYaw = Math.abs(MathHelper.wrapDegrees(entity.getYaw() - this.yaw)); + if (approachYaw > 80) { + return; + } + Vec3d offset = entity.getPos().subtract(source.getOriginVector()); - float yawDifference = pitch < 15 ? (180 - yaw + destination.yaw) : 0; - Vec3d dest = target.pos().add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.05, 0); + float yawDifference = pitch < 15 ? getYawDifference() : 0; + Vec3d dest = target.pos().add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.1, 0); + + if (entity.getWorld().isTopSolid(BlockPos.ofFloored(dest).up(), entity)) { + dest = dest.add(0, 1, 0); + } entity.resetPortalCooldown(); - entity.timeUntilRegen = 100; - entity.setYaw(entity.getYaw() + yawDifference); + float yaw = MathHelper.wrapDegrees(entity.getYaw() + yawDifference); + entity.setVelocity(entity.getVelocity().rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)); entity.getWorld().playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1); - entity.teleport(dest.x, dest.y, dest.z); + entity.teleport((ServerWorld)entity.getWorld(), dest.x, dest.y, dest.z, PositionFlag.VALUES, yaw, entity.getPitch()); entity.getWorld().playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1); setDirty(); + Living.updateVelocity(entity); + if (!source.subtractEnergyCost(Math.sqrt(entity.getPos().subtract(dest).length()))) { setDead(); } @@ -142,20 +184,15 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme return; } - Ether ether = Ether.get(source.asWorld()); - ether.getEntries(getType()) - .stream() - .filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet()) - .findAny() - .ifPresent(entry -> { + Ether.get(source.asWorld()).anyMatch(getType(), entry -> { + if (entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet()) { entry.setTaken(true); teleportationTarget.copyFrom(entry.entity); + targetPortalId = entry.getSpellId(); setDirty(); - }); - } - - private Optional getTarget(Caster source) { - return teleportationTarget.getTarget().flatMap(target -> Ether.get(source.asWorld()).getEntry(getType(), target.uuid())); + } + return false; + }); } @Override @@ -174,41 +211,40 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme LivingEntity caster = source.getMaster(); Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos()); parent.setOrientation(pitch, yaw); - entity.setPos(targetPos.x, caster.getY() + 1.5, targetPos.z); - } - - @Override - public void updatePlacement(Caster source, PlaceableSpell parent) { - parent.getParticleEffectAttachment(source).ifPresent(attachment -> { - attachment.setAttribute(Attachment.ATTR_RADIUS, 2); - attachment.setAttribute(Attachment.ATTR_OPACITY, 0.92F); - }); + entity.setPos(targetPos.x, Math.abs(pitch) > 15 ? targetPos.y : caster.getPos().y, targetPos.z); } @Override protected void onDestroyed(Caster caster) { - particleEffect.destroy(); Ether ether = Ether.get(caster.asWorld()); - ether.remove(getType(), caster.asEntity().getUuid()); + ether.remove(getType(), caster); getTarget(caster).ifPresent(e -> e.setTaken(false)); } @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); + if (targetPortalId != null) { + compound.putUuid("targetPortalId", targetPortalId); + } compound.putBoolean("publishedPosition", publishedPosition); compound.put("teleportationTarget", teleportationTarget.toNBT()); compound.putFloat("pitch", pitch); compound.putFloat("yaw", yaw); + compound.putFloat("targetPortalPitch", targetPortalPitch); + compound.putFloat("targetPortalYaw", targetPortalYaw); } @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); + targetPortalId = compound.containsUuid("targetPortalId") ? compound.getUuid("targetPortalId") : null; publishedPosition = compound.getBoolean("publishedPosition"); teleportationTarget.fromNBT(compound.getCompound("teleportationTarget")); pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); + targetPortalPitch = compound.getFloat("targetPortalPitch"); + targetPortalYaw = compound.getFloat("targetPortalYaw"); particleArea = PARTICLE_AREA.rotate( pitch * MathHelper.RADIANS_PER_DEGREE, (180 - yaw) * MathHelper.RADIANS_PER_DEGREE diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java index 61cc6e79..04831ef6 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect; +import java.util.Optional; + import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; @@ -10,11 +12,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect; -import com.minelittlepony.unicopia.particle.ParticleHandle; -import com.minelittlepony.unicopia.particle.SphereParticleEffect; -import com.minelittlepony.unicopia.particle.UParticles; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; +import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.projectile.ProjectileUtil; import com.minelittlepony.unicopia.util.shape.Sphere; @@ -30,6 +30,7 @@ import net.minecraft.entity.passive.PassiveEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.vehicle.AbstractMinecartEntity; import net.minecraft.entity.vehicle.BoatEntity; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; public class ShieldSpell extends AbstractSpell { @@ -40,9 +41,16 @@ public class ShieldSpell extends AbstractSpell { .with(Trait.AIR, 9) .build(); - protected final ParticleHandle particlEffect = new ParticleHandle(); + private final TargetSelecter targetSelecter = new TargetSelecter(this).setFilter(this::isValidTarget); - private final TargetSelecter targetSelecter = new TargetSelecter(this); + private float prevRadius; + private float radius; + + private float rangeMultiplier; + private float targetRangeMultiplier; + + private int prevTicksDying; + private int ticksDying; protected ShieldSpell(CustomisedSpellType type) { super(type); @@ -53,33 +61,27 @@ public class ShieldSpell extends AbstractSpell { return method == CastingMethod.STAFF || getTraits().get(Trait.GENEROSITY) > 0 ? toPlaceable() : this; } - @Override - protected void onDestroyed(Caster caster) { - particlEffect.destroy(); - } - @Override public Affinity getAffinity() { return getTraits().get(Trait.DARKNESS) > 0 ? Affinity.BAD : Affinity.GOOD; } protected void generateParticles(Caster source) { - float radius = (float)getDrawDropOffRange(source); Vec3d origin = getOrigin(source); source.spawnParticles(origin, new Sphere(true, radius), (int)(radius * 6), pos -> { source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO); - }); - particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, getType().getColor(), 0.3F, radius), origin, Vec3d.ZERO); - }).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_RADIUS, radius); + if (source.asWorld().random.nextInt(10) == 0 && source.asWorld().random.nextFloat() < source.getCorruption().getScaled(1)) { + ParticleUtils.spawnParticle(source.asWorld(), new LightningBoltParticleEffect(true, 3, 2, 0.1F, Optional.empty()), pos, Vec3d.ZERO); + } }); } @Override public boolean tick(Caster source, Situation situation) { + prevRadius = radius; + radius = (float)getDrawDropOffRange(source); if (source.isClient()) { generateParticles(source); @@ -97,27 +99,53 @@ public class ShieldSpell extends AbstractSpell { long costMultiplier = applyEntities(source); if (costMultiplier > 0) { - double cost = 2 - source.getLevel().getScaled(2); - - cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F); - cost /= knowledge; - cost += getDrawDropOffRange(source) / 10F; - - if (!source.subtractEnergyCost(cost)) { - setDead(); - } + consumeManage(source, costMultiplier, knowledge); } return !isDead(); } + @Override + public void tickDying(Caster caster) { + prevTicksDying = ticksDying; + if (ticksDying++ > 25) { + super.tickDying(caster); + } + } + + protected void consumeManage(Caster source, long costMultiplier, float knowledge) { + double cost = 2 - source.getLevel().getScaled(2); + + cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F); + cost /= knowledge; + cost += radius / 10F; + + if (!source.subtractEnergyCost(cost)) { + setDead(); + } + } + + public float getRadius(float tickDelta) { + float base = MathHelper.lerp(tickDelta, prevRadius, radius); + float scale = MathHelper.clamp(MathHelper.lerp(tickDelta, prevTicksDying, ticksDying), 0, 1); + return base * scale; + } + /** * Calculates the maximum radius of the shield. aka The area of effect. */ public double getDrawDropOffRange(Caster source) { - float multiplier = source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2; + targetRangeMultiplier = source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2; + if (rangeMultiplier < targetRangeMultiplier - 0.1F) { + rangeMultiplier += 0.1F; + } else if (rangeMultiplier > targetRangeMultiplier + 0.1) { + rangeMultiplier -= 0.1F; + } else { + rangeMultiplier = targetRangeMultiplier; + } + float min = (source instanceof Pony ? 4 : 6) + getTraits().get(Trait.POWER); - double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / multiplier; + double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / rangeMultiplier; return range; } @@ -146,11 +174,8 @@ public class ShieldSpell extends AbstractSpell { } protected long applyEntities(Caster source) { - double radius = getDrawDropOffRange(source); - Vec3d origin = getOrigin(source); - - targetSelecter.getEntities(source, radius, this::isValidTarget).forEach(i -> { + targetSelecter.getEntities(source, radius).forEach(i -> { try { applyRadialEffect(source, i, i.getPos().distanceTo(origin), radius); } catch (Throwable e) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java index b2482157..87119eab 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java @@ -10,28 +10,32 @@ import java.util.stream.Stream; import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.ability.magic.Affine; import com.minelittlepony.unicopia.ability.magic.Caster; -import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.spell.Spell; -import com.minelittlepony.unicopia.entity.player.Pony; -import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import net.minecraft.entity.Entity; +import net.minecraft.predicate.entity.EntityPredicates; public class TargetSelecter { - private final Map targets = new TreeMap<>(); private final Spell spell; + private BiPredicate, Entity> filter = (a, b) -> true; + public TargetSelecter(Spell spell) { this.spell = spell; } - public Stream getEntities(Caster source, double radius, BiPredicate, Entity> filter) { + public TargetSelecter setFilter(BiPredicate, Entity> filter) { + this.filter = filter; + return this; + } + + public Stream getEntities(Caster source, double radius) { targets.values().removeIf(Target::tick); return source.findAllEntitiesInRange(radius) - .filter(entity -> entity.isAlive() && !entity.isRemoved() && notOwnerOrFriend(spell, source, entity) && !SpellPredicate.IS_SHIELD_LIKE.isOn(entity)) + .filter(EntityPredicates.VALID_ENTITY) .filter(EquinePredicates.EXCEPT_MAGIC_IMMUNE) - .filter(e -> filter.test(source, e)) + .filter(entity -> entity != source.asEntity() && validTarget(spell, source, entity) && filter.test(source, entity)) .map(i -> { targets.computeIfAbsent(i.getUuid(), Target::new); return i; @@ -42,30 +46,19 @@ public class TargetSelecter { return targets.values().stream().filter(Target::canHurt).count(); } - public static Predicate notOwnerOrFriend(Affine affine, Caster source) { - return target -> notOwnerOrFriend(affine, source, target); + public static Predicate validTarget(Affine affine, Caster source) { + return target -> validTarget(affine, source, target); } - public static Predicate isOwnerOrFriend(Affine affine, Caster source) { - return target -> isOwnerOrFriend(affine, source, target); - } - - public static boolean notOwnerOrFriend(Affine affine, Caster source, Entity target) { + public static boolean validTarget(Affine affine, Caster source, Entity target) { return !isOwnerOrFriend(affine, source, target); } - public static boolean isOwnerOrFriend(Affine affine, Caster source, Entity target) { - Entity owner = source.getMaster(); - - if (affine.isEnemy(source)) { - return FriendshipBraceletItem.isComrade(source, target); - } - - return FriendshipBraceletItem.isComrade(source, target) - || (owner != null && (Pony.equal(target, owner) || owner.isConnectedThroughVehicle(target))); + public static boolean isOwnerOrFriend(Affine affine, Caster source, Entity target) { + return affine.applyInversion(source, source.isOwnerOrFriend(target)); } - static final class Target { + private static final class Target { private int cooldown = 20; Target(UUID id) { } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/ItemWithTraits.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/ItemWithTraits.java new file mode 100644 index 00000000..5b6cd107 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/ItemWithTraits.java @@ -0,0 +1,5 @@ +package com.minelittlepony.unicopia.ability.magic.spell.trait; + +public interface ItemWithTraits { + SpellTraits getDefaultTraits(); +} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java index 58c24bc4..17cd9568 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java @@ -29,6 +29,7 @@ import net.fabricmc.api.Environment; import net.minecraft.block.Block; import net.minecraft.inventory.Inventory; import net.minecraft.item.Item; +import net.minecraft.item.SpawnEggItem; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; @@ -41,6 +42,7 @@ import net.minecraft.registry.Registries; public final class SpellTraits implements Iterable> { public static final SpellTraits EMPTY = new SpellTraits(Map.of()); + private static final SpellTraits SPAWN_EGG_TRAITS = new SpellTraits(Map.of(Trait.LIFE, 20F)); private static Map REGISTRY = new HashMap<>(); static final Map> ITEMS = new HashMap<>(); @@ -220,6 +222,12 @@ public final class SpellTraits implements Iterable> { } public static SpellTraits of(Item item) { + if (item instanceof ItemWithTraits i) { + return i.getDefaultTraits(); + } + if (item instanceof SpawnEggItem) { + return SPAWN_EGG_TRAITS; + } return REGISTRY.getOrDefault(Registries.ITEM.getId(item), EMPTY); } diff --git a/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java b/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java index 84cfd2b8..d8ef1910 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java @@ -14,7 +14,7 @@ import net.minecraft.util.math.*; import net.minecraft.util.math.random.Random; import net.minecraft.world.*; -public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock { +public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock, ZapStagedBlock { public static final MapCodec CODEC = LeavesBlock.createCodec(BaseZapAppleLeavesBlock::new); public static Settings settings() { @@ -39,61 +39,29 @@ public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock } @Override - public boolean hasRandomTicks(BlockState state) { - return !state.get(PERSISTENT); - } - - @Override - public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { - super.randomTick(state, world, pos, random); - tryAdvanceStage(state, world, pos, random); - } - - @Override - public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) { - if (state.get(PERSISTENT)) { - return state; + public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { + if (state.get(PERSISTENT) || oldState.isOf(state.getBlock())) { + return; } - - if (world instanceof ServerWorld sw) { - ZapAppleStageStore store = ZapAppleStageStore.get(sw); - ZapAppleStageStore.Stage currentStage = store.getStage(); - if (currentStage == ZapAppleStageStore.Stage.HIBERNATING) { - return currentStage.getNewState(state); - } - } - - return state; + updateStage(state, world, pos); } @Override 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)) { return; } - - ZapAppleStageStore store = ZapAppleStageStore.get(world); - ZapAppleStageStore.Stage newStage = store.getStage(); - if (!world.isDay() && getStage(state).mustChangeIntoInstantly(newStage)) { - world.setBlockState(pos, newStage.getNewState(state)); - onStageChanged(store, newStage, world, state, pos, random); - } + tryAdvanceStage(state, world, pos, random); } - protected ZapAppleStageStore.Stage getStage(BlockState state) { + @Override + public ZapAppleStageStore.Stage getStage(BlockState state) { return ZapAppleStageStore.Stage.FLOWERING; } @Override - protected boolean shouldDecay(BlockState state) { + protected final boolean shouldDecay(BlockState state) { return false; } @@ -124,40 +92,10 @@ public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock @Override public int getTint(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int foliageColor) { - if (pos == null) { return 0x4C7EFA; } return TintedBlock.blend(TintedBlock.rotate(foliageColor, 2), 0x0000FF, 0.3F); } - - static void onStageChanged(ZapAppleStageStore store, ZapAppleStageStore.Stage stage, ServerWorld world, BlockState state, BlockPos pos, Random random) { - boolean mustFruit = Random.create(state.getRenderingSeed(pos)).nextInt(5) < 2; - BlockState below = world.getBlockState(pos.down()); - - if (world.isAir(pos.down())) { - if (stage == ZapAppleStageStore.Stage.FRUITING && mustFruit) { - world.setBlockState(pos.down(), UBlocks.ZAP_BULB.getDefaultState(), Block.NOTIFY_ALL); - store.triggerLightningStrike(pos); - } - } - - if (stage != ZapAppleStageStore.Stage.HIBERNATING && world.getRandom().nextInt(10) == 0) { - store.triggerLightningStrike(pos); - } - - if (stage == ZapAppleStageStore.Stage.RIPE) { - if (below.isOf(UBlocks.ZAP_BULB)) { - world.setBlockState(pos.down(), UBlocks.ZAP_APPLE.getDefaultState(), Block.NOTIFY_ALL); - store.playMoonEffect(pos); - } - } - - if (mustFruit && stage == ZapAppleStageStore.Stage.HIBERNATING) { - if (below.isOf(UBlocks.ZAP_APPLE) || below.isOf(UBlocks.ZAP_BULB)) { - world.setBlockState(pos.down(), Blocks.AIR.getDefaultState()); - } - } - } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java new file mode 100644 index 00000000..d5d0dced --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java @@ -0,0 +1,66 @@ +package com.minelittlepony.unicopia.block; + +import java.util.List; + +import com.minelittlepony.unicopia.ability.EarthPonyGrowAbility.Growable; +import com.minelittlepony.unicopia.entity.mob.IgnominiousBulbEntity; +import com.minelittlepony.unicopia.particle.MagicParticleEffect; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.block.BlockState; +import net.minecraft.block.FlowerBlock; +import net.minecraft.block.SuspiciousStewIngredient; +import net.minecraft.client.util.ParticleUtil; +import net.minecraft.entity.Dismounting; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.World; + +public class CuringJokeBlock extends FlowerBlock implements Growable { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + STEW_EFFECT_CODEC.forGetter(FlowerBlock::getStewEffects), + FlowerBlock.createSettingsCodec() + ).apply(instance, CuringJokeBlock::new)); + + public CuringJokeBlock(StatusEffect suspiciousStewEffect, int effectDuration, Settings settings) { + super(suspiciousStewEffect, effectDuration, settings); + } + + protected CuringJokeBlock(List effects, Settings settings) { + super(effects, settings); + } + + @Override + public MapCodec getCodec() { + return CODEC; + } + + @Override + public void randomDisplayTick(BlockState state, World world, BlockPos pos, Random random) { + for (int i = 0; i < 3; i++) { + ParticleUtil.spawnParticle(world, pos, random, new MagicParticleEffect(0x3388EE)); + } + } + + @Override + public boolean grow(World world, BlockState state, BlockPos pos) { + var otherFlowers = BlockPos.streamOutwards(pos, 16, 16, 16) + .filter(p -> world.getBlockState(p).isOf(this)) + .map(BlockPos::toImmutable) + .toList(); + + IgnominiousBulbEntity bulb = new IgnominiousBulbEntity(world); + bulb.setBaby(true); + bulb.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); + + if (Dismounting.canPlaceEntityAt(world, bulb, bulb.getBoundingBox())) { + otherFlowers.forEach(p -> world.breakBlock(p, false)); + world.spawnEntity(bulb); + return true; + } + + return false; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java b/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java new file mode 100644 index 00000000..b450c828 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java @@ -0,0 +1,229 @@ +package com.minelittlepony.unicopia.block; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.minelittlepony.unicopia.entity.player.Pony; + +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.HayBlock; +import net.minecraft.block.ShapeContext; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.registry.tag.ItemTags; +import net.minecraft.sound.SoundCategory; +import net.minecraft.state.StateManager; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.Util; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; + +public class EdibleBlock extends HayBlock { + private static final List REGISTRY = new ArrayList<>(); + + static final BooleanProperty TOP_NORTH_EAST = BooleanProperty.of("top_north_east"); + static final BooleanProperty TOP_NORTH_WEST = BooleanProperty.of("top_north_west"); + static final BooleanProperty TOP_SOUTH_EAST = BooleanProperty.of("top_south_east"); + static final BooleanProperty TOP_SOUTH_WEST = BooleanProperty.of("top_south_west"); + + static final BooleanProperty BOTTOM_NORTH_EAST = BooleanProperty.of("bottom_north_east"); + static final BooleanProperty BOTTOM_NORTH_WEST = BooleanProperty.of("bottom_north_west"); + static final BooleanProperty BOTTOM_SOUTH_EAST = BooleanProperty.of("bottom_south_east"); + static final BooleanProperty BOTTOM_SOUTH_WEST = BooleanProperty.of("bottom_south_west"); + + // [up/down][north/south][west/east] + private static final BooleanProperty[] SEGMENTS = { + BOTTOM_NORTH_WEST, + BOTTOM_NORTH_EAST, + BOTTOM_SOUTH_WEST, + BOTTOM_SOUTH_EAST, + TOP_NORTH_WEST, + TOP_NORTH_EAST, + TOP_SOUTH_WEST, + TOP_SOUTH_EAST + }; + private static final VoxelShape[] SHAPES = { + Block.createCuboidShape(0, 0, 0, 8, 8, 8), + Block.createCuboidShape(8, 0, 0, 16, 8, 8), + Block.createCuboidShape(0, 0, 8, 8, 8, 16), + Block.createCuboidShape(8, 0, 8, 16, 8, 16), + Block.createCuboidShape(0, 8, 0, 8, 16, 8), + Block.createCuboidShape(8, 8, 0, 16, 16, 8), + Block.createCuboidShape(0, 8, 8, 8, 16, 16), + Block.createCuboidShape(8, 8, 8, 16, 16, 16) + }; + private static final Function SHAPE_CACHE = Util.memoize(state -> { + @Nullable + VoxelShape shape = null; + for (int i = 0; i < SEGMENTS.length; i++) { + if (state.get(SEGMENTS[i])) { + shape = shape == null ? SHAPES[i] : VoxelShapes.union(shape, SHAPES[i]); + } + } + return shape == null ? VoxelShapes.fullCube() : shape.simplify(); + }); + + static void bootstrap() { + UseBlockCallback.EVENT.register((PlayerEntity player, World world, Hand hand, BlockHitResult hitResult) -> { + if (!Pony.of(player).getSpecies().isEquine() + || (player.shouldCancelInteraction() && (!player.getMainHandStack().isEmpty() || !player.getOffHandStack().isEmpty()))) { + return ActionResult.PASS; + } + + BlockPos pos = hitResult.getBlockPos(); + BlockState state = world.getBlockState(pos); + + for (EdibleBlock edibleBlock : REGISTRY) { + Block match = edibleBlock.getBaseBlock(); + if (match != Blocks.AIR && state.isOf(match)) { + ActionResult result = StateUtil.copyState(state, edibleBlock.getDefaultState()).onUse(world, player, hand, hitResult); + + if (result.isAccepted()) { + return result; + } + } + } + + return ActionResult.PASS; + }); + } + + private final Identifier baseBlock; + private final Identifier material; + + public EdibleBlock(Identifier baseBlock, Identifier material, boolean register) { + super(Settings.copy(Blocks.HAY_BLOCK)); + for (BooleanProperty segment : SEGMENTS) { + setDefaultState(getDefaultState().with(segment, true)); + } + this.baseBlock = baseBlock; + this.material = material; + if (register) { + REGISTRY.add(this); + FlammableBlockRegistry.getDefaultInstance().add(this, 60, 20); + } + } + + public Block getBaseBlock() { + return Registries.BLOCK.get(baseBlock); + } + + @Override + public String getTranslationKey() { + return getBaseBlock().getTranslationKey(); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder); + builder.add(SEGMENTS); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return SHAPE_CACHE.apply(state); + } + + @Override + @Deprecated + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + if (player.isSpectator()) { + return ActionResult.FAIL; + } + + ItemStack stack = player.getStackInHand(hand); + + if (!stack.isEmpty() && stack.isOf(Registries.ITEM.get(material))) { + BooleanProperty segment = getHitCorner(hit, 1); + + if (!state.get(segment)) { + if (!player.isCreative()) { + stack.decrement(1); + } + if (!world.isClient) { + state = state.with(segment, true); + if (SHAPE_CACHE.apply(state) == VoxelShapes.fullCube()) { + state = StateUtil.copyState(state, getBaseBlock().getDefaultState()); + } + world.setBlockState(pos, state); + } + world.playSound(player, pos, getSoundGroup(state).getPlaceSound(), SoundCategory.BLOCKS); + + return ActionResult.SUCCESS; + } + + return ActionResult.FAIL; + } + + BooleanProperty corner = getHitCorner(hit, -1); + + if (!state.get(corner)) { + return ActionResult.PASS; + } + + boolean usingHoe = stack.isIn(ItemTags.HOES); + + if (!usingHoe) { + if (!(player.isCreative() || player.getHungerManager().isNotFull()) || !player.isSneaking()) { + return ActionResult.FAIL; + } + } + + if (!world.isClient) { + state = state.with(corner, false); + if (SHAPE_CACHE.apply(state) == VoxelShapes.fullCube()) { + world.removeBlock(pos, false); + } else { + world.setBlockState(pos, state); + } + } + + if (usingHoe) { + stack.damage(1, player, p -> p.sendToolBreakStatus(hand)); + dropStack(world, pos, Registries.ITEM.get(material).getDefaultStack()); + player.playSound(USounds.Vanilla.ITEM_HOE_TILL, 1, 1); + } else { + player.playSound(USounds.Vanilla.ENTITY_GENERIC_EAT, 1, 1); + if (world.random.nextInt(10) == 0) { + player.playSound(USounds.Vanilla.ENTITY_PLAYER_BURP, 1, player.getSoundPitch()); + } + player.getHungerManager().add(4, 2.3F); + } + return ActionResult.SUCCESS; + } + + static BooleanProperty getHitCorner(BlockHitResult hit, int direction) { + Vec3d pos = hit.getPos().add(Vec3d.of(hit.getSide().getVector()).multiply(direction * 0.001F)); + + BlockPos bPos = hit.getBlockPos(); + + return SEGMENTS[ + (4 * getIndex(pos.y, bPos.getY())) + + (2 * getIndex(pos.z, bPos.getZ())) + + (getIndex(pos.x, bPos.getX())) + ]; + } + + static int getIndex(double axisHit, int tile) { + axisHit -= tile; + return Math.abs(axisHit) > 0.5 ? 1 : 0; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java b/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java new file mode 100644 index 00000000..15c2f6ad --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java @@ -0,0 +1,31 @@ +package com.minelittlepony.unicopia.block; + +import com.mojang.serialization.MapCodec; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.state.StateManager; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; + +public class EnchantedFruitBlock extends FruitBlock { + static final BooleanProperty ENCHANTED = BooleanProperty.of("enchanted"); + + private static final MapCodec CODEC = createCodec(EnchantedFruitBlock::new); + + public EnchantedFruitBlock(Direction attachmentFace, Block stem, VoxelShape shape, boolean flammable, Settings settings) { + super(attachmentFace, stem, shape, flammable, settings); + setDefaultState(getDefaultState().with(ENCHANTED, false)); + } + + @Override + public MapCodec getCodec() { + return CODEC; + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder); + builder.add(ENCHANTED); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/FancyBedBlock.java b/src/main/java/com/minelittlepony/unicopia/block/FancyBedBlock.java index 802394b6..7b74d5ec 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/FancyBedBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/FancyBedBlock.java @@ -52,7 +52,7 @@ public class FancyBedBlock extends BedBlock { )) ); - private final String base; + protected final String base; public FancyBedBlock(String base, Settings settings) { super(DyeColor.WHITE, settings); diff --git a/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java b/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java index 0e49ae15..a1279a39 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java @@ -42,10 +42,10 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka public static final List REGISTRY = new ArrayList<>(); - private final Supplier fruit; - private final Supplier rottenFruitSupplier; + protected final Supplier fruit; + protected final Supplier rottenFruitSupplier; - private final int overlay; + protected final int overlay; public FruitBearingBlock(int overlay, Supplier fruit, Supplier rottenFruitSupplier, Settings settings) { super(settings @@ -78,6 +78,14 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka return true; } + protected boolean shouldAdvance(Random random) { + return true; + } + + protected BlockState getPlacedFruitState(Random random) { + return fruit.get().getDefaultState(); + } + @Override public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { super.randomTick(state, world, pos, random); @@ -86,10 +94,14 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka return; } - if (world.isDay()) { + if (world.getBaseLightLevel(pos, 0) > 8) { BlockSoundGroup group = getSoundGroup(state); int steps = FertilizableUtil.getGrowthSteps(world, pos, state, random); while (steps-- > 0) { + if (!shouldAdvance(random)) { + continue; + } + if (state.get(STAGE) == Stage.FRUITING) { state = state.cycle(AGE); if (state.get(AGE) > 20) { @@ -105,7 +117,7 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka if (stage == Stage.FRUITING && isPositionValidForFruit(state, pos)) { if (world.isAir(fruitPosition)) { - world.setBlockState(fruitPosition, fruit.get().getDefaultState(), Block.NOTIFY_ALL); + world.setBlockState(fruitPosition, getPlacedFruitState(random), Block.NOTIFY_ALL); } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/FruitBlock.java b/src/main/java/com/minelittlepony/unicopia/block/FruitBlock.java index 2458d5bb..9aed5d1b 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/FruitBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/FruitBlock.java @@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.block; import java.util.List; import com.minelittlepony.unicopia.ability.EarthPonyKickAbility.Buckable; +import com.mojang.datafixers.util.Function5; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; @@ -23,20 +24,24 @@ public class FruitBlock extends Block implements Buckable { public static final int DEFAULT_FRUIT_SIZE = 5; public static final double DEFAULT_STEM_OFFSET = 2.6F; public static final VoxelShape DEFAULT_SHAPE = createFruitShape(DEFAULT_STEM_OFFSET, DEFAULT_FRUIT_SIZE); - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Direction.CODEC.fieldOf("attachment_face").forGetter(b -> b.attachmentFace), - Registries.BLOCK.getCodec().fieldOf("stem").forGetter(b -> b.stem), - RecordCodecBuilder.create(i -> i.group( - Codec.DOUBLE.fieldOf("stem_offset").forGetter(b -> (double)0), - Codec.DOUBLE.fieldOf("fruit_offset").forGetter(b -> (double)0) - ).apply(i, FruitBlock::createFruitShape)).fieldOf("shape").forGetter(b -> b.shape), - Codec.BOOL.fieldOf("flammable").forGetter(b -> false), - BedBlock.createSettingsCodec() - ).apply(instance, FruitBlock::new)); + private static final MapCodec CODEC = createCodec(FruitBlock::new); - private final Direction attachmentFace; - private final Block stem; - private final VoxelShape shape; + protected final Direction attachmentFace; + protected final Block stem; + protected final VoxelShape shape; + + public static MapCodec createCodec(Function5 constructor) { + return RecordCodecBuilder.mapCodec(instance -> instance.group( + Direction.CODEC.fieldOf("attachment_face").forGetter(b -> b.attachmentFace), + Registries.BLOCK.getCodec().fieldOf("stem").forGetter(b -> b.stem), + RecordCodecBuilder.create(i -> i.group( + Codec.DOUBLE.fieldOf("stem_offset").forGetter(b -> (double)0), + Codec.DOUBLE.fieldOf("fruit_offset").forGetter(b -> (double)0) + ).apply(i, FruitBlock::createFruitShape)).fieldOf("shape").forGetter(b -> b.shape), + Codec.BOOL.fieldOf("flammable").forGetter(b -> false), + BedBlock.createSettingsCodec() + ).apply(instance, constructor)); + } public static VoxelShape createFruitShape(double stemOffset, double fruitSize) { final double min = (16 - fruitSize) * 0.5; diff --git a/src/main/java/com/minelittlepony/unicopia/block/GoldenOakLeavesBlock.java b/src/main/java/com/minelittlepony/unicopia/block/GoldenOakLeavesBlock.java new file mode 100644 index 00000000..01f30449 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/GoldenOakLeavesBlock.java @@ -0,0 +1,43 @@ +package com.minelittlepony.unicopia.block; + +import java.util.function.Supplier; + +import com.minelittlepony.unicopia.util.CodecUtils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.block.BedBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.util.math.random.Random; + +public class GoldenOakLeavesBlock extends FruitBearingBlock { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.INT.fieldOf("overlay").forGetter(b -> b.overlay), + CodecUtils.supplierOf(Registries.BLOCK.getCodec()).fieldOf("fruit").forGetter(b -> b.fruit), + CodecUtils.supplierOf(ItemStack.CODEC).fieldOf("rotten_fruit").forGetter(b -> b.rottenFruitSupplier), + BedBlock.createSettingsCodec() + ).apply(instance, GoldenOakLeavesBlock::new)); + + public GoldenOakLeavesBlock(int overlay, Supplier fruit, Supplier rottenFruitSupplier, Settings settings) { + super(overlay, fruit, rottenFruitSupplier, settings); + } + + @Override + public MapCodec getCodec() { + return CODEC; + } + + @Override + protected boolean shouldAdvance(Random random) { + return random.nextInt(1000) == 0; + } + + @Override + protected BlockState getPlacedFruitState(Random random) { + return super.getPlacedFruitState(random).with(EnchantedFruitBlock.ENCHANTED, random.nextInt(1000) == 0); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java index 1fa1ec1a..5f0d7ab2 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java +++ b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java @@ -21,6 +21,7 @@ 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.entity.effect.UEffects; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.cloud.CloudBlockItem; import com.minelittlepony.unicopia.item.group.ItemGroupRegistry; @@ -28,6 +29,7 @@ import com.minelittlepony.unicopia.server.world.UTreeGen; import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; import net.fabricmc.fabric.api.registry.StrippableBlockRegistry; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.block.*; import net.minecraft.block.AbstractBlock.Settings; import net.minecraft.block.entity.BlockEntityType; @@ -136,6 +138,15 @@ public interface UBlocks { () -> UItems.APPLE_PIE_HOOF, Settings.create().solid().mapColor(MapColor.ORANGE).strength(0.5F).sounds(BlockSoundGroup.WOOL).pistonBehavior(PistonBehavior.DESTROY) )); + Block GOLDEN_OAK_LEAVES = register("golden_oak_leaves", new GoldenOakLeavesBlock( + MapColor.GOLD.color, + () -> UBlocks.GOLDEN_APPLE, + () -> Items.GOLDEN_APPLE.getDefaultStack(), + FabricBlockSettings.copy(Blocks.OAK_LEAVES) + ), ItemGroups.NATURAL); + Block GOLDEN_APPLE = register("golden_apple", new EnchantedFruitBlock(Direction.DOWN, GOLDEN_OAK_LEAVES, FruitBlock.DEFAULT_SHAPE, false, Settings.create().mapColor(MapColor.GOLD))); + Block GOLDEN_OAK_SPROUT = register("golden_oak_sprout", new SproutBlock(0xE5FFCC88, () -> UItems.GOLDEN_OAK_SEEDS, () -> UTreeGen.GOLDEN_APPLE_TREE.sapling().map(Block::getDefaultState).get(), SproutBlock.settings())); + Block GOLDEN_OAK_LOG = register("golden_oak_log", BlockConstructionUtils.createLogBlock(MapColor.OFF_WHITE, MapColor.GOLD), ItemGroups.BUILDING_BLOCKS); SegmentedCropBlock OATS = register("oats", SegmentedCropBlock.create(11, 5, () -> UItems.OAT_SEEDS, null, null, AbstractBlock.Settings.copy(Blocks.WHEAT))); SegmentedCropBlock OATS_STEM = register("oats_stem", OATS.createNext(5)); @@ -143,6 +154,13 @@ public interface UBlocks { Block PLUNDER_VINE = register("plunder_vine", new ThornBlock(() -> UBlocks.PLUNDER_VINE_BUD, Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).ticksRandomly().sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY))); Block PLUNDER_VINE_BUD = register("plunder_vine_bud", new ThornBudBlock(PLUNDER_VINE.getDefaultState(), Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).nonOpaque().ticksRandomly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY))); + CuringJokeBlock CURING_JOKE = register("curing_joke", new CuringJokeBlock(UEffects.BUTTER_FINGERS, 7, AbstractBlock.Settings.create().mapColor(MapColor.PALE_PURPLE).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).offset(AbstractBlock.OffsetType.XZ).pistonBehavior(PistonBehavior.DESTROY))); + Block GOLD_ROOT = register("gold_root", new CarrotsBlock(AbstractBlock.Settings.create().mapColor(MapColor.GOLD).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP).pistonBehavior(PistonBehavior.DESTROY)) { + @Override + protected ItemConvertible getSeedsItem() { + return Items.GOLDEN_CARROT; + } + }); 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(() -> CHITIN, Settings.copy(CHITIN)), ItemGroups.NATURAL); @@ -156,49 +174,42 @@ public interface UBlocks { 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, + Block CLOUD = register("cloud", new NaturalCloudBlock(true, () -> UBlocks.SOGGY_CLOUD, - () -> UBlocks.COMPACTED_CLOUD), ItemGroups.NATURAL); + () -> UBlocks.COMPACTED_CLOUD, + Settings.create().mapColor(MapColor.OFF_WHITE).hardness(0.3F).resistance(0).sounds(BlockSoundGroup.WOOL)), 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); - PoreousCloudStairsBlock CLOUD_STAIRS = register("cloud_stairs", new PoreousCloudStairsBlock(CLOUD.getDefaultState(), Settings.copy(CLOUD), () -> UBlocks.SOGGY_CLOUD_STAIRS), ItemGroups.NATURAL); + Block CLOUD_SLAB = register("cloud_slab", new CloudSlabBlock(true, () -> UBlocks.SOGGY_CLOUD_SLAB, Settings.copy(CLOUD)), ItemGroups.NATURAL); + PoreousCloudStairsBlock CLOUD_STAIRS = register("cloud_stairs", new PoreousCloudStairsBlock(CLOUD.getDefaultState(), () -> UBlocks.SOGGY_CLOUD_STAIRS, Settings.copy(CLOUD)), 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 = register("cloud_planks", new NaturalCloudBlock(false, null, () -> UBlocks.COMPACTED_CLOUD_PLANKS, Settings.copy(CLOUD).hardness(0.4F).requiresTool().solid()), ItemGroups.BUILDING_BLOCKS); 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_SLAB = register("cloud_plank_slab", new CloudSlabBlock(false, null, Settings.copy(CLOUD_PLANKS)), 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 CLOUD_BRICKS = register("cloud_bricks", new NaturalCloudBlock(false, null, () -> UBlocks.COMPACTED_CLOUD_BRICKS, Settings.copy(CLOUD).hardness(0.6F).requiresTool().solid()), 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_SLAB = register("cloud_brick_slab", new CloudSlabBlock(false, null, Settings.copy(CLOUD_BRICKS)), 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 ETCHED_CLOUD = register("etched_cloud", new NaturalCloudBlock(false, null, () -> UBlocks.COMPACTED_CLOUD_BRICKS, Settings.copy(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_SLAB = register("etched_cloud_slab", new CloudSlabBlock(false, null, Settings.copy(ETCHED_CLOUD)), 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)); + SoggyCloudBlock SOGGY_CLOUD = register("soggy_cloud", new SoggyCloudBlock(() -> UBlocks.CLOUD, Settings.copy(CLOUD).hardness(0.7F))); + SoggyCloudSlabBlock SOGGY_CLOUD_SLAB = register("soggy_cloud_slab", new SoggyCloudSlabBlock(() -> UBlocks.CLOUD_SLAB, Settings.copy(SOGGY_CLOUD))); + SoggyCloudStairsBlock SOGGY_CLOUD_STAIRS = register("soggy_cloud_stairs", new SoggyCloudStairsBlock(SOGGY_CLOUD.getDefaultState(), () -> UBlocks.CLOUD_STAIRS, Settings.copy(CLOUD))); - 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 DENSE_CLOUD = register("dense_cloud", new NaturalCloudBlock(false, null, () -> UBlocks.COMPACTED_DENSE_CLOUD, Settings.create().mapColor(MapColor.GRAY).hardness(0.5F).resistance(0).sounds(BlockSoundGroup.WOOL).solid()), 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_SLAB = register("dense_cloud_slab", new CloudSlabBlock(false, null, Settings.copy(DENSE_CLOUD)), 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 CARVED_CLOUD = register("carved_cloud", new OrientedCloudBlock(false, Settings.copy(CLOUD).hardness(0.4F).requiresTool().solid()), ItemGroups.BUILDING_BLOCKS); Block UNSTABLE_CLOUD = register("unstable_cloud", new UnstableCloudBlock(Settings.copy(CLOUD)), ItemGroups.NATURAL); 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 CLOUD_CHEST = register("cloud_chest", new CloudChestBlock(DENSE_CLOUD.getDefaultState(), Settings.copy(DENSE_CLOUD).instrument(Instrument.BASS).strength(2.5f)), 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))); @@ -209,7 +220,9 @@ public interface UBlocks { Block STABLE_DOOR = register("stable_door", new StableDoorBlock(BlockSetType.OAK, Settings.copy(Blocks.OAK_DOOR)), ItemGroups.FUNCTIONAL); Block DARK_OAK_DOOR = register("dark_oak_stable_door", new StableDoorBlock(BlockSetType.OAK, Settings.copy(Blocks.OAK_DOOR)), ItemGroups.FUNCTIONAL); Block CRYSTAL_DOOR = register("crystal_door", new CrystalDoorBlock(UWoodTypes.CRYSTAL, Settings.copy(Blocks.IRON_DOOR)), ItemGroups.FUNCTIONAL); - Block CLOUD_DOOR = register("cloud_door", new CloudDoorBlock(Settings.copy(CLOUD), CLOUD.getDefaultState(), UWoodTypes.CLOUD), ItemGroups.FUNCTIONAL); + Block CLOUD_DOOR = register("cloud_door", new CloudDoorBlock(CLOUD.getDefaultState(), UWoodTypes.CLOUD, Settings.copy(CLOUD)), ItemGroups.FUNCTIONAL); + + EdibleBlock HAY_BLOCK = register("hay_block", new EdibleBlock(new Identifier("hay_block"), new Identifier("wheat"), true)); private static T register(String name, T item) { return register(Unicopia.id(name), item); @@ -234,10 +247,15 @@ public interface UBlocks { if (block instanceof CloudLike || block instanceof SlimePustuleBlock || block instanceof PileBlock) { SEMI_TRANSPARENT_BLOCKS.add(block); } + return Registry.register(Registries.BLOCK, id, block); } static void bootstrap() { + if (FabricLoader.getInstance().isModLoaded("farmersdelight")) { + register("rice_block", new EdibleBlock(new Identifier("farmersdelight", "rice_bale"), new Identifier("farmersdelight", "rice_panicle"), true)); + register("straw_block", new EdibleBlock(new Identifier("farmersdelight", "straw_bale"), new Identifier("farmersdelight", "straw"), true)); + } BlockEntityTypeSupportHelper.of(BlockEntityType.SIGN).addSupportedBlocks(PALM_SIGN, PALM_WALL_SIGN); BlockEntityTypeSupportHelper.of(BlockEntityType.HANGING_SIGN).addSupportedBlocks(PALM_HANGING_SIGN, PALM_WALL_HANGING_SIGN); @@ -245,21 +263,24 @@ 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, PLUNDER_VINE, PLUNDER_VINE_BUD, CLAM_SHELL, SCALLOP_SHELL, TURRET_SHELL); + Collections.addAll(TRANSLUCENT_BLOCKS, WEATHER_VANE, CHITIN_SPIKES, PLUNDER_VINE, PLUNDER_VINE_BUD, CLAM_SHELL, SCALLOP_SHELL, TURRET_SHELL, CURING_JOKE); TintedBlock.REGISTRY.add(PALM_LEAVES); FlammableBlockRegistry.getDefaultInstance().add(GREEN_APPLE_LEAVES, 30, 60); FlammableBlockRegistry.getDefaultInstance().add(SWEET_APPLE_LEAVES, 30, 60); FlammableBlockRegistry.getDefaultInstance().add(SOUR_APPLE_LEAVES, 30, 60); + FlammableBlockRegistry.getDefaultInstance().add(GOLDEN_OAK_LEAVES, 60, 120); FlammableBlockRegistry.getDefaultInstance().add(MANGO_LEAVES, 30, 60); FlammableBlockRegistry.getDefaultInstance().add(PALM_LEAVES, 30, 60); FlammableBlockRegistry.getDefaultInstance().add(PALM_LOG, 5, 5); FlammableBlockRegistry.getDefaultInstance().add(PALM_WOOD, 5, 5); + FlammableBlockRegistry.getDefaultInstance().add(GOLDEN_OAK_LOG, 15, 15); FlammableBlockRegistry.getDefaultInstance().add(STRIPPED_PALM_LOG, 5, 5); FlammableBlockRegistry.getDefaultInstance().add(STRIPPED_PALM_WOOD, 5, 5); FlammableBlockRegistry.getDefaultInstance().add(PALM_PLANKS, 5, 20); FlammableBlockRegistry.getDefaultInstance().add(BANANAS, 5, 20); UBlockEntities.bootstrap(); + EdibleBlock.bootstrap(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/UWoodTypes.java b/src/main/java/com/minelittlepony/unicopia/block/UWoodTypes.java index 0a44586e..5f76ed97 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/UWoodTypes.java +++ b/src/main/java/com/minelittlepony/unicopia/block/UWoodTypes.java @@ -15,6 +15,7 @@ import net.minecraft.util.Identifier; public interface UWoodTypes { WoodType PALM = register("palm"); + WoodType GOLDEN_OAK = register("golden_oak"); RegistryKey PALM_BOAT_TYPE = TerraformBoatTypeRegistry.createKey(Unicopia.id("palm")); BlockSetType CLOUD = new BlockSetTypeBuilder() diff --git a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java index 0a0baea8..ffdbe38a 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java @@ -5,11 +5,8 @@ import com.mojang.serialization.MapCodec; 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.*; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock { public static final MapCodec CODEC = createCodec(ZapAppleLeavesBlock::new); @@ -25,31 +22,13 @@ public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock { return CODEC; } - @Override - public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { - 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) - || !(world instanceof ServerWorld sw)) { - return; - } - - ZapAppleStageStore store = ZapAppleStageStore.get(sw); - ZapAppleStageStore.Stage currentStage = store.getStage(); - if (currentStage != getStage(state)) { - world.setBlockState(pos, currentStage.getNewState(state)); - } - } - @Override public BlockState getPlacementState(ItemPlacementContext ctx) { return super.getPlacementState(ctx).with(STAGE, ZapAppleStageStore.Stage.GREENING); } @Override - protected ZapAppleStageStore.Stage getStage(BlockState state) { + public ZapAppleStageStore.Stage getStage(BlockState state) { return state.get(STAGE); } diff --git a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java index 7d30ce32..b2434136 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java @@ -1,15 +1,16 @@ package com.minelittlepony.unicopia.block; import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore.Stage; import com.mojang.serialization.MapCodec; import net.minecraft.block.*; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.*; import net.minecraft.util.math.random.Random; -import net.minecraft.world.WorldAccess; +import net.minecraft.world.World; -public class ZapAppleLeavesPlaceholderBlock extends AirBlock { +public class ZapAppleLeavesPlaceholderBlock extends AirBlock implements ZapStagedBlock { public static final MapCodec CODEC = createCodec(ZapAppleLeavesPlaceholderBlock::new); ZapAppleLeavesPlaceholderBlock(Settings settings) { @@ -23,37 +24,19 @@ public class ZapAppleLeavesPlaceholderBlock extends AirBlock { } @Override - public boolean hasRandomTicks(BlockState state) { - return true; + public Stage getStage(BlockState state) { + return ZapAppleStageStore.Stage.HIBERNATING; } @Override - public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) { - - if (world instanceof ServerWorld sw) { - ZapAppleStageStore store = ZapAppleStageStore.get(sw); - ZapAppleStageStore.Stage currentStage = store.getStage(); - if (currentStage != ZapAppleStageStore.Stage.HIBERNATING) { - return currentStage.getNewState(state); - } - } - - return state; + public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { + updateStage(state, world, pos); } @Deprecated @Override public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { super.scheduledTick(state, world, pos, random); - - ZapAppleStageStore store = ZapAppleStageStore.get(world); - ZapAppleStageStore.Stage newStage = store.getStage(); - if (!world.isDay() && ZapAppleStageStore.Stage.HIBERNATING.mustChangeIntoInstantly(newStage)) { - state = newStage.getNewState(state); - world.setBlockState(pos, state); - BaseZapAppleLeavesBlock.onStageChanged(store, newStage, world, state, pos, random); - } - - world.scheduleBlockTick(pos, state.getBlock(), 1); + tryAdvanceStage(state, world, pos, random); } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java new file mode 100644 index 00000000..5e3f41d4 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java @@ -0,0 +1,87 @@ +package com.minelittlepony.unicopia.block; + +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.World; + +public interface ZapStagedBlock { + ZapAppleStageStore.Stage getStage(BlockState state); + + default void updateStage(BlockState state, World world, BlockPos pos) { + if (!(world instanceof ServerWorld sw)) { + return; + } + ZapAppleStageStore.Stage currentStage = ZapAppleStageStore.get(sw).getStage(); + if (currentStage != getStage(state)) { + state = getState(currentStage); + world.setBlockState(pos, state); + } + world.scheduleBlockTick(pos, state.getBlock(), 1); + } + + default void tryAdvanceStage(BlockState state, ServerWorld world, BlockPos pos, Random random) { + ZapAppleStageStore store = ZapAppleStageStore.get(world); + ZapAppleStageStore.Stage currentStage = store.getStage(); + if (!world.isDay() && currentStage != getStage(state)) { + int transitionRate = getTransitionRate(currentStage); + if (transitionRate == 0 || random.nextInt(transitionRate) == 0) { + state = getState(currentStage); + world.setBlockState(pos, state); + onStageChanged(store, currentStage, world, state, pos, random); + } + } + world.scheduleBlockTick(pos, state.getBlock(), 1); + } + + default int getTransitionRate(ZapAppleStageStore.Stage stage) { + if (stage == ZapAppleStageStore.Stage.HIBERNATING || stage == ZapAppleStageStore.Stage.GREENING) { + return 10; + } + return 2500; + } + + default BlockState getState(ZapAppleStageStore.Stage stage) { + if (stage == ZapAppleStageStore.Stage.HIBERNATING) { + return UBlocks.ZAP_LEAVES_PLACEHOLDER.getDefaultState(); + } + if (stage == ZapAppleStageStore.Stage.FLOWERING) { + return UBlocks.FLOWERING_ZAP_LEAVES.getDefaultState(); + } + return UBlocks.ZAP_LEAVES.getDefaultState().with(ZapAppleLeavesBlock.STAGE, stage); + } + + private static void onStageChanged(ZapAppleStageStore store, ZapAppleStageStore.Stage stage, ServerWorld world, BlockState state, BlockPos pos, Random random) { + boolean mustFruit = Random.create(state.getRenderingSeed(pos)).nextInt(5) < 2; + BlockState below = world.getBlockState(pos.down()); + + if (world.isAir(pos.down())) { + if (stage == ZapAppleStageStore.Stage.FRUITING && mustFruit) { + world.setBlockState(pos.down(), UBlocks.ZAP_BULB.getDefaultState(), Block.NOTIFY_ALL); + store.triggerLightningStrike(pos); + } + } + + if (stage != ZapAppleStageStore.Stage.HIBERNATING && world.getRandom().nextInt(10) == 0) { + store.triggerLightningStrike(pos); + } + + if (stage == ZapAppleStageStore.Stage.RIPE) { + if (below.isOf(UBlocks.ZAP_BULB)) { + world.setBlockState(pos.down(), UBlocks.ZAP_APPLE.getDefaultState(), Block.NOTIFY_ALL); + store.playMoonEffect(pos); + } + } + + if (mustFruit && stage == ZapAppleStageStore.Stage.HIBERNATING) { + if (below.isOf(UBlocks.ZAP_APPLE) || below.isOf(UBlocks.ZAP_BULB)) { + world.setBlockState(pos.down(), Blocks.AIR.getDefaultState()); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudBedBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudBedBlock.java index 6ecc1497..78f79309 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudBedBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudBedBlock.java @@ -4,6 +4,11 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; import com.minelittlepony.unicopia.block.FancyBedBlock; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.block.BedBlock; import net.minecraft.block.BlockState; import net.minecraft.block.ShapeContext; import net.minecraft.entity.Entity; @@ -20,6 +25,12 @@ import net.minecraft.world.BlockView; import net.minecraft.world.World; public class CloudBedBlock extends FancyBedBlock implements CloudLike { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.STRING.fieldOf("base").forGetter(b -> b.base), + BlockState.CODEC.fieldOf("base_state").forGetter(b -> b.baseState), + BedBlock.createSettingsCodec() + ).apply(instance, CloudBedBlock::new)); + private final BlockState baseState; private final CloudBlock baseBlock; @@ -29,6 +40,12 @@ public class CloudBedBlock extends FancyBedBlock implements CloudLike { this.baseBlock = (CloudBlock)baseState.getBlock(); } + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public MapCodec getCodec() { + return (MapCodec)CODEC; + } + @Override public final VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { if (!baseBlock.canInteract(baseState, world, pos, EquineContext.of(context))) { diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudBlock.java index 490d7e4d..5453677d 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudBlock.java @@ -4,7 +4,11 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; import com.minelittlepony.unicopia.entity.player.Pony; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.BedBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.ShapeContext; @@ -27,13 +31,23 @@ import net.minecraft.world.LightType; import net.minecraft.world.World; public class CloudBlock extends Block implements CloudLike { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable), + BedBlock.createSettingsCodec() + ).apply(instance, CloudBlock::new)); + protected final boolean meltable; - public CloudBlock(Settings settings, boolean meltable) { + public CloudBlock(boolean meltable, Settings settings) { super((meltable ? settings.ticksRandomly() : settings).nonOpaque()); this.meltable = meltable; } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override public void onEntityLand(BlockView world, Entity entity) { boolean bounce = Math.abs(entity.getVelocity().y) > 0.3; diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudChestBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudChestBlock.java index 86aa498d..6fa559e3 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudChestBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudChestBlock.java @@ -6,10 +6,14 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; import com.minelittlepony.unicopia.block.UBlockEntities; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + import net.minecraft.block.BlockState; import net.minecraft.block.ChestBlock; import net.minecraft.block.DoubleBlockProperties; import net.minecraft.block.ShapeContext; +import net.minecraft.block.StairsBlock; import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.block.entity.ChestBlockEntity; @@ -33,6 +37,10 @@ import net.minecraft.world.BlockView; import net.minecraft.world.World; public class CloudChestBlock extends ChestBlock implements CloudLike { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseState), + StairsBlock.createSettingsCodec() + ).apply(instance, CloudChestBlock::new)); private final BlockState baseState; private final CloudBlock baseBlock; @@ -76,12 +84,17 @@ public class CloudChestBlock extends ChestBlock implements CloudLike { } }; - public CloudChestBlock(Settings settings, BlockState baseState) { + public CloudChestBlock(BlockState baseState, Settings settings) { super(settings, () -> UBlockEntities.CLOUD_CHEST); this.baseState = baseState; this.baseBlock = (CloudBlock)baseState.getBlock(); } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { return new TileData(pos, state); diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudDoorBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudDoorBlock.java index dce7d864..b1749d42 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudDoorBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudDoorBlock.java @@ -4,6 +4,8 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; import com.minelittlepony.unicopia.Race; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.block.BlockSetType; import net.minecraft.block.BlockState; @@ -22,15 +24,24 @@ import net.minecraft.world.BlockView; import net.minecraft.world.World; public class CloudDoorBlock extends DoorBlock implements CloudLike { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BlockState.CODEC.fieldOf("base_state").forGetter(b -> b.baseState), + BlockSetType.CODEC.fieldOf("block_set_type").forGetter(DoorBlock::getBlockSetType), + DoorBlock.createSettingsCodec() + ).apply(instance, CloudDoorBlock::new)); private final BlockState baseState; private final CloudBlock baseBlock; - public CloudDoorBlock(Settings settings, BlockState baseState, BlockSetType blockSet) { + public CloudDoorBlock(BlockState baseState, BlockSetType blockSet, Settings settings) { super(blockSet, settings); this.baseState = baseState; this.baseBlock = (CloudBlock)baseState.getBlock(); } + @Override + public MapCodec getCodec() { + return CODEC; + } @Override public final VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudPillarBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudPillarBlock.java index 5655b652..722b4d60 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudPillarBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudPillarBlock.java @@ -5,6 +5,7 @@ import java.util.Map; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; +import com.mojang.serialization.MapCodec; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -20,6 +21,7 @@ import net.minecraft.world.BlockView; import net.minecraft.world.WorldAccess; public class CloudPillarBlock extends CloudBlock { + private static final MapCodec CODEC = Block.createCodec(CloudPillarBlock::new); private static final BooleanProperty NORTH = BooleanProperty.of("north"); private static final BooleanProperty SOUTH = BooleanProperty.of("south"); private static final Map DIRECTION_PROPERTIES = Map.of( @@ -41,10 +43,15 @@ public class CloudPillarBlock extends CloudBlock { // [1,0] [1,1] public CloudPillarBlock(Settings settings) { - super(settings, false); + super(false, settings); setDefaultState(getDefaultState().with(NORTH, true).with(SOUTH, true)); } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override protected void appendProperties(StateManager.Builder builder) { builder.add(NORTH, SOUTH); diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudSlabBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudSlabBlock.java index e1204745..c445809b 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudSlabBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudSlabBlock.java @@ -5,7 +5,12 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; +import com.minelittlepony.unicopia.util.CodecUtils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.BedBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.ShapeContext; @@ -24,14 +29,24 @@ import net.minecraft.world.BlockView; import net.minecraft.world.WorldAccess; public class CloudSlabBlock extends WaterloggableCloudBlock { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable), + CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock), + BedBlock.createSettingsCodec() + ).apply(instance, WaterloggableCloudBlock::new)); private static final VoxelShape BOTTOM_SHAPE = Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 8.0, 16.0); private static final VoxelShape TOP_SHAPE = Block.createCuboidShape(0.0, 8.0, 0.0, 16.0, 16.0, 16.0); - public CloudSlabBlock(Settings settings, boolean meltable, @Nullable Supplier soggyBlock) { - super(settings, meltable, soggyBlock); + public CloudSlabBlock(boolean meltable, @Nullable Supplier soggyBlock, Settings settings) { + super(meltable, soggyBlock, settings); setDefaultState(getDefaultState().with(SlabBlock.TYPE, SlabType.BOTTOM)); } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override public boolean hasSidedTransparency(BlockState state) { return state.get(SlabBlock.TYPE) != SlabType.DOUBLE; diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudStairsBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudStairsBlock.java index bafd9e21..1c127f92 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudStairsBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/CloudStairsBlock.java @@ -3,6 +3,9 @@ package com.minelittlepony.unicopia.block.cloud; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + import net.minecraft.block.BlockState; import net.minecraft.block.ShapeContext; import net.minecraft.block.StairsBlock; @@ -17,6 +20,10 @@ import net.minecraft.world.BlockView; import net.minecraft.world.World; public class CloudStairsBlock extends StairsBlock implements CloudLike { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseBlockState), + StairsBlock.createSettingsCodec() + ).apply(instance, CloudStairsBlock::new)); private final CloudBlock baseBlock; @@ -25,6 +32,11 @@ public class CloudStairsBlock extends StairsBlock implements CloudLike { this.baseBlock = (CloudBlock)baseState.getBlock(); } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override public void onEntityLand(BlockView world, Entity entity) { baseBlock.onEntityLand(world, entity); diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/CompactedCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/CompactedCloudBlock.java index 50d27fe1..86456378 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/CompactedCloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/CompactedCloudBlock.java @@ -5,6 +5,8 @@ import java.util.Map; import java.util.function.Function; import com.minelittlepony.unicopia.EquineContext; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -32,6 +34,9 @@ import net.minecraft.world.World; import net.minecraft.world.WorldView; public class CompactedCloudBlock extends CloudBlock { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseState) + ).apply(instance, CompactedCloudBlock::new)); static final Map FACING_PROPERTIES = ConnectingBlock.FACING_PROPERTIES; static final Collection PROPERTIES = FACING_PROPERTIES.values(); @@ -49,13 +54,18 @@ public class CompactedCloudBlock extends CloudBlock { private final BlockState baseState; public CompactedCloudBlock(BlockState baseState) { - super(Settings.copy(baseState.getBlock()).dropsLike(baseState.getBlock()), true); + super(true, Settings.copy(baseState.getBlock()).dropsLike(baseState.getBlock())); this.baseState = baseState; PROPERTIES.forEach(property -> { setDefaultState(getDefaultState().with(property, true)); }); } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override public ItemStack getPickStack(WorldView world, BlockPos pos, BlockState state) { return baseState.getBlock().getPickStack(world, pos, baseState); diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/NaturalCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/NaturalCloudBlock.java index 2d7325dc..de0ec27e 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/NaturalCloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/NaturalCloudBlock.java @@ -4,10 +4,17 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.util.CodecUtils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.block.BedBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; import net.minecraft.registry.tag.ItemTags; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvents; @@ -19,16 +26,28 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class NaturalCloudBlock extends PoreousCloudBlock { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable), + CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock), + CodecUtils.supplierOf(Registries.BLOCK.getCodec()).fieldOf("compacted_block").forGetter(b -> b.compactedBlock), + BedBlock.createSettingsCodec() + ).apply(instance, NaturalCloudBlock::new)); private final Supplier compactedBlock; - public NaturalCloudBlock(Settings settings, boolean meltable, + public NaturalCloudBlock(boolean meltable, @Nullable Supplier soggyBlock, - Supplier compactedBlock) { - super(settings.nonOpaque(), meltable, soggyBlock); + Supplier compactedBlock, + Settings settings) { + super(meltable, soggyBlock, settings.nonOpaque()); this.compactedBlock = compactedBlock; } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { ItemStack stack = player.getStackInHand(hand); diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/OrientedCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/OrientedCloudBlock.java index 2d3886db..52cd3d79 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/OrientedCloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/OrientedCloudBlock.java @@ -1,7 +1,11 @@ package com.minelittlepony.unicopia.block.cloud; import com.minelittlepony.unicopia.EquineContext; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.BedBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.item.ItemPlacementContext; @@ -13,13 +17,22 @@ import net.minecraft.util.BlockRotation; import net.minecraft.util.math.Direction; public class OrientedCloudBlock extends CloudBlock { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable), + BedBlock.createSettingsCodec() + ).apply(instance, OrientedCloudBlock::new)); public static final DirectionProperty FACING = Properties.FACING; - public OrientedCloudBlock(Settings settings, boolean meltable) { - super(settings, meltable); + public OrientedCloudBlock(boolean meltable, Settings settings) { + super(meltable, settings); this.setDefaultState(getDefaultState().with(FACING, Direction.UP)); } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override protected void appendProperties(StateManager.Builder builder) { builder.add(FACING); diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java index 5e17ca06..fdc1aaa6 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java @@ -4,25 +4,43 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.minelittlepony.unicopia.util.CodecUtils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.block.BedBlock; import net.minecraft.block.BlockState; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.random.Random; public class PoreousCloudBlock extends CloudBlock implements Soakable { - @Nullable - private final Supplier soggyBlock; + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable), + CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock), + BedBlock.createSettingsCodec() + ).apply(instance, PoreousCloudBlock::new)); - public PoreousCloudBlock(Settings settings, boolean meltable, @Nullable Supplier soggyBlock) { - super(settings.nonOpaque(), meltable); + @Nullable + protected final Supplier soggyBlock; + + public PoreousCloudBlock(boolean meltable, @Nullable Supplier soggyBlock, Settings settings) { + super(meltable, settings.nonOpaque()); this.soggyBlock = soggyBlock; } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Nullable @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, getDefaultState()); + return StateUtil.copyState(state, getDefaultState()); } return soggyBlock == null ? null : soggyBlock.get().getStateWithMoisture(state, moisture); } diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java index 6733ee2d..1ff03471 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java @@ -4,22 +4,38 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.minelittlepony.unicopia.util.CodecUtils; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + import net.minecraft.block.BlockState; +import net.minecraft.block.StairsBlock; public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakable { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseBlockState), + CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock), + StairsBlock.createSettingsCodec() + ).apply(instance, PoreousCloudStairsBlock::new)); protected final Supplier soggyBlock; - public PoreousCloudStairsBlock(BlockState baseState, Settings settings, Supplier soggyBlock) { + public PoreousCloudStairsBlock(BlockState baseState, Supplier soggyBlock, Settings settings) { super(baseState, settings); this.soggyBlock = soggyBlock; } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Nullable @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, getDefaultState()); + return StateUtil.copyState(state, getDefaultState()); } return soggyBlock.get().getStateWithMoisture(state, moisture); } diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/ShapingBenchBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/ShapingBenchBlock.java index 06c47ceb..4e3d7a68 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/ShapingBenchBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/ShapingBenchBlock.java @@ -4,6 +4,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; import com.minelittlepony.unicopia.container.ShapingBenchScreenHandler; +import com.mojang.serialization.MapCodec; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -23,6 +24,7 @@ import net.minecraft.world.BlockView; import net.minecraft.world.World; public class ShapingBenchBlock extends CloudBlock { + private static final MapCodec CODEC = Block.createCodec(ShapingBenchBlock::new); private static final VoxelShape SHAPE = VoxelShapes.union( Block.createCuboidShape(0, 13, 0, 3, 18, 3), Block.createCuboidShape(13, 13, 0, 16, 18, 3), @@ -34,7 +36,12 @@ public class ShapingBenchBlock extends CloudBlock { ); public ShapingBenchBlock(Settings settings) { - super(settings, false); + super(false, settings); + } + + @Override + public MapCodec getCodec() { + return CODEC; } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java index e800bbf8..866d5a29 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java @@ -1,21 +1,22 @@ package com.minelittlepony.unicopia.block.cloud; import java.util.Arrays; - import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; +import com.mojang.serialization.Codec; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.particle.ParticleTypes; +import net.minecraft.registry.Registries; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvents; import net.minecraft.state.property.IntProperty; -import net.minecraft.state.property.Property; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.Util; @@ -27,6 +28,8 @@ import net.minecraft.world.World; import net.minecraft.world.event.GameEvent; public interface Soakable { + Codec CODEC = Registries.BLOCK.getCodec().xmap(b -> (Soakable)b, s -> (Block)s); + IntProperty MOISTURE = IntProperty.of("moisture", 1, 7); Direction[] DIRECTIONS = Arrays.stream(Direction.values()).filter(d -> d != Direction.UP).toArray(Direction[]::new); @@ -105,15 +108,4 @@ public interface Soakable { 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, @Nullable BlockState to) { - if (to != null) { - for (Property property : from.getProperties()) { - to = to.withIfExists(property, from.get(property)); - } - } - return to; - } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java index 94b713ee..bb177f18 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java @@ -4,10 +4,17 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.minelittlepony.unicopia.util.CodecUtils; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.block.BedBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; import net.minecraft.server.world.ServerWorld; import net.minecraft.state.StateManager; import net.minecraft.util.ActionResult; @@ -19,15 +26,24 @@ import net.minecraft.world.World; import net.minecraft.world.WorldView; public class SoggyCloudBlock extends CloudBlock implements Soakable { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + CodecUtils.supplierOf(Registries.BLOCK.getCodec()).fieldOf("dry_block").forGetter(b -> b.dryBlock), + BedBlock.createSettingsCodec() + ).apply(instance, SoggyCloudBlock::new)); private final Supplier dryBlock; - public SoggyCloudBlock(Settings settings, Supplier dryBlock) { - super(settings.ticksRandomly(), false); + public SoggyCloudBlock(Supplier dryBlock, Settings settings) { + super(false, settings.ticksRandomly()); setDefaultState(getDefaultState().with(MOISTURE, 7)); this.dryBlock = dryBlock; } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override protected void appendProperties(StateManager.Builder builder) { super.appendProperties(builder); @@ -43,9 +59,9 @@ public class SoggyCloudBlock extends CloudBlock implements Soakable { @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, dryBlock.get().getDefaultState()); + return StateUtil.copyState(state, dryBlock.get().getDefaultState()); } - return Soakable.copyProperties(state, getDefaultState()).with(MOISTURE, moisture); + return StateUtil.copyState(state, getDefaultState()).with(MOISTURE, moisture); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java index c858c82c..6867ce41 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java @@ -4,10 +4,17 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.minelittlepony.unicopia.util.CodecUtils; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.block.BedBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; import net.minecraft.server.world.ServerWorld; import net.minecraft.state.StateManager; import net.minecraft.util.ActionResult; @@ -19,15 +26,24 @@ import net.minecraft.world.World; import net.minecraft.world.WorldView; public class SoggyCloudSlabBlock extends CloudSlabBlock { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + CodecUtils.supplierOf(Registries.BLOCK.getCodec()).fieldOf("dry_block").forGetter(b -> b.dryBlock), + BedBlock.createSettingsCodec() + ).apply(instance, SoggyCloudSlabBlock::new)); private final Supplier dryBlock; - public SoggyCloudSlabBlock(Settings settings, Supplier dryBlock) { - super(settings.ticksRandomly(), false, null); + public SoggyCloudSlabBlock(Supplier dryBlock, Settings settings) { + super(false, null, settings.ticksRandomly()); setDefaultState(getDefaultState().with(MOISTURE, 7)); this.dryBlock = dryBlock; } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override protected void appendProperties(StateManager.Builder builder) { super.appendProperties(builder); @@ -43,9 +59,9 @@ public class SoggyCloudSlabBlock extends CloudSlabBlock { @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, dryBlock.get().getDefaultState()); + return StateUtil.copyState(state, dryBlock.get().getDefaultState()); } - return Soakable.copyProperties(state, getDefaultState()).with(MOISTURE, moisture); + return StateUtil.copyState(state, getDefaultState()).with(MOISTURE, moisture); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java index 3a5b973a..fe3da5ea 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java @@ -4,23 +4,40 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.minelittlepony.unicopia.util.CodecUtils; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.StairsBlock; import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; import net.minecraft.state.StateManager; import net.minecraft.util.math.BlockPos; import net.minecraft.world.WorldView; public class SoggyCloudStairsBlock extends CloudStairsBlock implements Soakable { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseBlockState), + CodecUtils.supplierOf(Registries.BLOCK.getCodec()).optionalFieldOf("soggy_block", null).forGetter(b -> b.dryBlock), + StairsBlock.createSettingsCodec() + ).apply(instance, SoggyCloudStairsBlock::new)); private final Supplier dryBlock; - public SoggyCloudStairsBlock(BlockState baseState, Settings settings, Supplier dryBlock) { + public SoggyCloudStairsBlock(BlockState baseState, Supplier dryBlock, Settings settings) { super(baseState, settings); setDefaultState(getDefaultState().with(MOISTURE, 7)); this.dryBlock = dryBlock; } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override protected void appendProperties(StateManager.Builder builder) { super.appendProperties(builder); @@ -36,8 +53,8 @@ public class SoggyCloudStairsBlock extends CloudStairsBlock implements Soakable @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, dryBlock.get().getDefaultState()); + return StateUtil.copyState(state, dryBlock.get().getDefaultState()); } - return Soakable.copyProperties(state, getDefaultState()).with(MOISTURE, moisture); + return StateUtil.copyState(state, getDefaultState()).with(MOISTURE, moisture); } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/UnstableCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/UnstableCloudBlock.java index 30bf49c4..7f552eba 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/UnstableCloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/UnstableCloudBlock.java @@ -5,6 +5,7 @@ import java.util.Optional; import com.minelittlepony.unicopia.entity.mob.StormCloudEntity; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.mojang.serialization.MapCodec; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -28,14 +29,20 @@ import net.minecraft.world.poi.PointOfInterestStorage; import net.minecraft.world.poi.PointOfInterestTypes; public class UnstableCloudBlock extends CloudBlock { + private static final MapCodec CODEC = Block.createCodec(UnstableCloudBlock::new); private static final int MAX_CHARGE = 6; private static final IntProperty CHARGE = IntProperty.of("charge", 0, MAX_CHARGE); public UnstableCloudBlock(Settings settings) { - super(settings, false); + super(false, settings); setDefaultState(getDefaultState().with(CHARGE, 0)); } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override protected void appendProperties(StateManager.Builder builder) { builder.add(CHARGE); diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/WaterloggableCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/WaterloggableCloudBlock.java index 38efed62..915ea406 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/WaterloggableCloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/WaterloggableCloudBlock.java @@ -5,7 +5,12 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; +import com.minelittlepony.unicopia.util.CodecUtils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.BedBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Waterloggable; @@ -23,13 +28,24 @@ import net.minecraft.world.BlockView; import net.minecraft.world.WorldAccess; public class WaterloggableCloudBlock extends PoreousCloudBlock implements Waterloggable { + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable), + CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock), + BedBlock.createSettingsCodec() + ).apply(instance, WaterloggableCloudBlock::new)); + public static final BooleanProperty WATERLOGGED = Properties.WATERLOGGED; - public WaterloggableCloudBlock(Settings settings, boolean meltable, @Nullable Supplier soggyBlock) { - super(settings, meltable, soggyBlock); + public WaterloggableCloudBlock(boolean meltable, @Nullable Supplier soggyBlock, Settings settings) { + super(meltable, soggyBlock, settings); setDefaultState(getDefaultState().with(WATERLOGGED, false)); } + @Override + public MapCodec getCodec() { + return CODEC; + } + @Override protected void appendProperties(StateManager.Builder builder) { builder.add(WATERLOGGED); diff --git a/src/main/java/com/minelittlepony/unicopia/block/state/StateChange.java b/src/main/java/com/minelittlepony/unicopia/block/state/StateChange.java index 72e7b9e4..972eef4f 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/state/StateChange.java +++ b/src/main/java/com/minelittlepony/unicopia/block/state/StateChange.java @@ -11,7 +11,6 @@ import com.minelittlepony.unicopia.Unicopia; import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.state.property.Property; import net.minecraft.util.Identifier; import net.minecraft.util.JsonHelper; import net.minecraft.registry.Registries; @@ -50,7 +49,7 @@ public abstract class StateChange { return state; } return Registries.BLOCK.getOrEmpty(id).map(Block::getDefaultState) - .map(newState -> merge(newState, state)) + .map(newState -> StateUtil.copyState(state, newState)) .orElse(state); } }; @@ -101,17 +100,4 @@ public abstract class StateChange { return serializer.apply(json); }).orElseThrow(() -> new IllegalArgumentException("Invalid action " + action)); } - - private static BlockState merge(BlockState into, BlockState from) { - for (var property : from.getProperties()) { - if (into.contains(property)) { - into = copy(into, from, property); - } - } - return into; - } - - private static > BlockState copy(BlockState to, BlockState from, Property property) { - return to.with(property, from.get(property)); - } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/state/StateUtil.java b/src/main/java/com/minelittlepony/unicopia/block/state/StateUtil.java new file mode 100644 index 00000000..42334368 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/state/StateUtil.java @@ -0,0 +1,19 @@ +package com.minelittlepony.unicopia.block.state; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.block.BlockState; +import net.minecraft.state.property.Property; + +public interface StateUtil { + @SuppressWarnings({ "unchecked", "rawtypes" }) + static BlockState copyState(BlockState from, @Nullable BlockState to) { + if (to == null) { + return to; + } + for (var property : from.getProperties()) { + to = to.withIfExists((Property)property, from.get(property)); + } + return to; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java index 0be0da61..837b65fd 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java @@ -2,7 +2,7 @@ package com.minelittlepony.unicopia.client; import java.lang.ref.WeakReference; import java.util.Map; -import java.util.Optional; +import java.util.UUID; import java.util.function.Predicate; import java.util.function.Supplier; @@ -13,19 +13,20 @@ import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.FlightType; import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.USounds; -import com.minelittlepony.unicopia.ability.magic.CasterView; import com.minelittlepony.unicopia.client.gui.DismissSpellScreen; import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters; +import com.minelittlepony.unicopia.client.particle.ClientBoundParticleSpawner; import com.minelittlepony.unicopia.client.sound.*; import com.minelittlepony.unicopia.entity.player.PlayerPhysics; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity; -import com.minelittlepony.unicopia.server.world.Ether; +import com.minelittlepony.unicopia.particle.ParticleSpawner; import com.mojang.authlib.GameProfile; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.sound.AggressiveBeeSoundInstance; import net.minecraft.client.sound.MovingMinecartSoundInstance; import net.minecraft.client.sound.PassiveBeeSoundInstance; @@ -37,32 +38,21 @@ import net.minecraft.entity.passive.BeeEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.vehicle.AbstractMinecartEntity; import net.minecraft.network.PacketByteBuf; -import net.minecraft.server.world.ServerWorld; +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket; import net.minecraft.sound.SoundCategory; import net.minecraft.util.Identifier; import net.minecraft.util.math.random.Random; -import net.minecraft.world.BlockView; import net.minecraft.world.World; public class ClientInteractionManager extends InteractionManager { private final MinecraftClient client = MinecraftClient.getInstance(); - private final Optional clientWorld = Optional.of(() -> MinecraftClient.getInstance().world); - private final Int2ObjectMap> playingSounds = new Int2ObjectOpenHashMap<>(); - @Override - public Optional getCasterView(BlockView view) { - if (view instanceof ServerWorld world) { - return Optional.of(Ether.get(world)); - } - return clientWorld; - } - @Override public Map readChapters(PacketByteBuf buffer) { - return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter); + return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter); } @Override @@ -159,4 +149,16 @@ public class ClientInteractionManager extends InteractionManager { public int getViewMode() { return client.options.getPerspective().ordinal(); } + + @Override + public ParticleSpawner createBoundParticle(UUID id) { + return new ClientBoundParticleSpawner(id); + } + + @Override + public void sendPlayerLookAngles(PlayerEntity player) { + if (player instanceof ClientPlayerEntity c) { + c.networkHandler.sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(player.getYaw(), player.getPitch(), player.isOnGround())); + } + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 5bf94a22..ab09e503 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -10,6 +10,8 @@ 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; +import com.minelittlepony.unicopia.client.particle.DustCloudParticle; +import com.minelittlepony.unicopia.client.particle.FloatingBubbleParticle; import com.minelittlepony.unicopia.client.particle.GroundPoundParticle; import com.minelittlepony.unicopia.client.particle.HealthDrainParticle; import com.minelittlepony.unicopia.client.particle.LightningBoltParticle; @@ -22,6 +24,7 @@ import com.minelittlepony.unicopia.client.particle.ShockwaveParticle; import com.minelittlepony.unicopia.client.particle.SphereParticle; import com.minelittlepony.unicopia.client.render.*; import com.minelittlepony.unicopia.client.render.entity.*; +import com.minelittlepony.unicopia.client.render.shader.UShaders; import com.minelittlepony.unicopia.client.render.spell.SpellRendererFactory; import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.item.ChameleonItem; @@ -59,12 +62,14 @@ import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import net.minecraft.world.BlockRenderView; +@SuppressWarnings("deprecation") 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)); + ParticleFactoryRegistry.getInstance().register(UParticles.BUBBLE, createFactory(FloatingBubbleParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.RAIN_DROPS, createFactory(RaindropsParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create)); ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new); @@ -76,6 +81,7 @@ public interface URenderers { ParticleFactoryRegistry.getInstance().register(UParticles.GROUND_POUND, GroundPoundParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.CLOUDS_ESCAPING, CloudsEscapingParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.LIGHTNING_BOLT, LightningBoltParticle::new); + ParticleFactoryRegistry.getInstance().register(UParticles.DUST_CLOUD, DustCloudParticle::new); AccessoryFeatureRenderer.register( BraceletFeatureRenderer::new, AmuletFeatureRenderer::new, GlassesFeatureRenderer::new, @@ -97,6 +103,8 @@ public interface URenderers { EntityRendererRegistry.register(UEntities.AIR_BALLOON, AirBalloonEntityRenderer::new); EntityRendererRegistry.register(UEntities.FRIENDLY_CREEPER, FriendlyCreeperEntityRenderer::new); EntityRendererRegistry.register(UEntities.LOOT_BUG, LootBugEntityRenderer::new); + EntityRendererRegistry.register(UEntities.TENTACLE, TentacleEntityRenderer::new); + EntityRendererRegistry.register(UEntities.IGNOMINIOUS_BULB, IgnominiousBulbEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.WEATHER_VANE, WeatherVaneBlockEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.FANCY_BED, CloudBedBlockEntityRenderer::new); @@ -108,6 +116,7 @@ public interface URenderers { 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()); + ModelPredicateProviderRegistry.register(Unicopia.id("zap_cycle"), (stack, world, entity, seed) -> UnicopiaClient.getInstance().getZapStageDelta()); 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)); @@ -123,6 +132,7 @@ public interface URenderers { TerraformBoatClientHelper.registerModelLayers(Unicopia.id("palm"), false); SpellRendererFactory.bootstrap(); + UShaders.bootstrap(); } private static void register(DynamicItemRenderer renderer, ItemConvertible...items) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java index 32b01a8e..9ea6a700 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java +++ b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java @@ -22,6 +22,7 @@ import com.minelittlepony.unicopia.entity.player.PlayerCamera; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.network.handler.ClientNetworkHandlerImpl; import com.minelittlepony.unicopia.server.world.WeatherConditions; +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; import com.minelittlepony.unicopia.util.Lerp; import net.fabricmc.api.ClientModInitializer; @@ -53,6 +54,9 @@ public class UnicopiaClient implements ClientModInitializer { public final Lerp tangentalSkyAngle = new Lerp(0, true); public final Lerp skyAngle = new Lerp(0, true); + private ZapAppleStageStore.Stage zapAppleStage = ZapAppleStageStore.Stage.HIBERNATING; + private long zapStageTime; + public static Optional getCamera() { PlayerEntity player = MinecraftClient.getInstance().player; @@ -84,6 +88,15 @@ public class UnicopiaClient implements ClientModInitializer { instance = this; } + public void setZapAppleStage(ZapAppleStageStore.Stage stage, long delta) { + zapAppleStage = stage; + zapStageTime = delta; + } + + public float getZapStageDelta() { + return zapAppleStage.getCycleProgress(zapStageTime); + } + public float getSkyAngleDelta(float tickDelta) { if (MinecraftClient.getInstance().world == null) { return 0; @@ -135,6 +148,8 @@ public class UnicopiaClient implements ClientModInitializer { world.setRainGradient(gradient); world.setThunderGradient(gradient); } + + zapStageTime++; } private Float getTargetRainGradient(ClientWorld world, BlockPos pos, float tickDelta) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/MagicText.java b/src/main/java/com/minelittlepony/unicopia/client/gui/MagicText.java new file mode 100644 index 00000000..4387217f --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/MagicText.java @@ -0,0 +1,16 @@ +package com.minelittlepony.unicopia.client.gui; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.math.MathHelper; + +public interface MagicText { + static int getColor() { + MinecraftClient client = MinecraftClient.getInstance(); + float ticks = client.player.age + client.getTickDelta(); + + float sin = (MathHelper.sin(ticks / 10F) + 1) * 155 * 0.25F; + float cos = (MathHelper.cos((ticks + 10) / 10F) + 1) * 155 * 0.25F; + + return (int)(sin + cos); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/ParagraphWrappingVisitor.java b/src/main/java/com/minelittlepony/unicopia/client/gui/ParagraphWrappingVisitor.java index 0a947e9c..66f38d23 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/ParagraphWrappingVisitor.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/ParagraphWrappingVisitor.java @@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.client.gui; import java.util.*; import java.util.function.BiConsumer; - import it.unimi.dsi.fastutil.ints.Int2IntFunction; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextHandler; @@ -31,12 +30,18 @@ public class ParagraphWrappingVisitor implements StyledVisitor { } @Override - public Optional accept(Style style, String s) { + public Optional accept(Style initialStyle, String s) { + StyleBreakingVisitor visitor = new StyleBreakingVisitor(initialStyle, this::acceptFragment); + TextVisitFactory.visitFormatted(s, 0, initialStyle, initialStyle, visitor); + visitor.flush(); + return Optional.empty(); + } + private Optional acceptFragment(Style inlineStyle, String s) { int remainingLength = (int)(pageWidth - currentLineCollectedLength); while (!s.isEmpty()) { - int trimmedLength = handler.getTrimmedLength(s, remainingLength, style); + int trimmedLength = handler.getTrimmedLength(s, remainingLength, inlineStyle); int newline = s.indexOf('\n'); if (newline >= 0 && newline < trimmedLength) { @@ -57,7 +62,7 @@ public class ParagraphWrappingVisitor implements StyledVisitor { trimmedLength = lastSpace > 0 ? Math.min(lastSpace, trimmedLength) : trimmedLength; } - Text fragment = Text.literal(s.substring(0, trimmedLength).trim()).setStyle(style); + Text fragment = Text.literal(s.substring(0, trimmedLength).trim()).setStyle(inlineStyle); float grabbedWidth = handler.getWidth(fragment); // advance if appending the next segment would cause an overflow @@ -101,14 +106,42 @@ public class ParagraphWrappingVisitor implements StyledVisitor { } public void advance() { + line++; if (progressedNonEmpty || currentLineCollectedLength > 0) { progressedNonEmpty = true; - lineConsumer.accept(currentLine, (++line) * font.fontHeight); + lineConsumer.accept(currentLine, line * font.fontHeight); } - pageWidth = widthSupplier.applyAsInt((++line) * font.fontHeight); + pageWidth = widthSupplier.applyAsInt(line * font.fontHeight); currentLine = Text.empty(); currentLineCollectedLength = 0; } - record StyledString (String string, Style style) {} + static final class StyleBreakingVisitor implements CharacterVisitor { + private final StyledVisitor fragmentVisitor; + private final StringBuilder collectedText = new StringBuilder(); + + private Style currentStyle; + + public StyleBreakingVisitor(Style initialStyle, StyledVisitor fragmentVisitor) { + this.currentStyle = initialStyle; + this.fragmentVisitor = fragmentVisitor; + } + + @Override + public boolean accept(int index, Style style, int codepoint) { + if (!style.equals(currentStyle)) { + flush(); + } + currentStyle = style; + collectedText.append((char)codepoint); + return true; + } + + public void flush() { + if (collectedText.length() > 0) { + fragmentVisitor.accept(currentStyle, collectedText.toString()); + collectedText.setLength(0); + } + } + } } \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java b/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java index 8cde15c2..c731c559 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java @@ -7,6 +7,8 @@ import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.ability.*; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.spell.DarkVortexSpellRenderer; import com.minelittlepony.unicopia.client.sound.*; import com.minelittlepony.unicopia.entity.ItemTracker; import com.minelittlepony.unicopia.entity.effect.EffectUtils; @@ -193,6 +195,19 @@ public class UHud { protected void renderViewEffects(Pony pony, DrawContext context, int scaledWidth, int scaledHeight, float tickDelta) { + float vortexDistortion = DarkVortexSpellRenderer.getCameraDistortion(); + + if (vortexDistortion > 20) { + context.fill(RenderLayers.getEndPortal(), 0, 0, scaledWidth, scaledHeight, 0); + context.getMatrices().push(); + context.getMatrices().translate(scaledWidth / 2, scaledHeight / 2, 0); + DrawableUtil.drawArc(context.getMatrices(), 0, 20, 0, MathHelper.TAU, 0x000000FF, false); + context.getMatrices().pop(); + return; + } else if (vortexDistortion > 0) { + context.fill(0, 0, scaledWidth, scaledHeight, (int)((vortexDistortion / 20F) * 255) << 24); + } + boolean hasEffect = client.player.hasStatusEffect(UEffects.SUN_BLINDNESS); ItemStack glasses = GlassesItem.getForEntity(client.player); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java index 98e3f345..fa7c365e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java @@ -5,6 +5,7 @@ import com.minelittlepony.common.client.gui.ScrollContainer; import com.minelittlepony.common.client.gui.element.Label; import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe; import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.client.gui.MagicText; import com.minelittlepony.unicopia.container.SpellbookState; import com.minelittlepony.unicopia.item.URecipes; import com.mojang.blaze3d.systems.RenderSystem; @@ -43,13 +44,10 @@ public class SpellbookCraftingPageContent extends ScrollContainer implements Spe @Override public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) { - - int headerColor = mouseY % 255; - - DrawableUtil.drawScaledText(context, state.getOffset() == 0 ? INVENTORY_TITLE : RECIPES_TITLE, screen.getFrameBounds().left + screen.getFrameBounds().width / 2 + 20, SpellbookScreen.TITLE_Y, 1.3F, headerColor); + DrawableUtil.drawScaledText(context, state.getOffset() == 0 ? INVENTORY_TITLE : RECIPES_TITLE, screen.getFrameBounds().left + screen.getFrameBounds().width / 2 + 20, SpellbookScreen.TITLE_Y, 1.3F, MagicText.getColor()); Text pageText = Text.translatable("%s/%s", state.getOffset() + 1, TOTAL_PAGES); - context.drawText(textRenderer, pageText, (int)(337 - textRenderer.getWidth(pageText) / 2F), 190, headerColor, false); + context.drawText(textRenderer, pageText, (int)(337 - textRenderer.getWidth(pageText) / 2F), 190, MagicText.getColor(), false); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookProfilePageContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookProfilePageContent.java index 42ac5a74..f630b9f4 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookProfilePageContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookProfilePageContent.java @@ -72,7 +72,7 @@ public class SpellbookProfilePageContent implements SpellbookChapterList.Content float currentScaledLevel = pony.getLevel().getScaled(1); float currentCorruption = pony.getCorruption().getScaled(1); - DrawableUtil.drawScaledText(context, pony.asEntity().getName(), SpellbookScreen.TITLE_X, y, 1.3F, SpellbookScreen.TITLE_COLOR); + DrawableUtil.drawScaledText(context, pony.asEntity().getName(), SpellbookScreen.TITLE_X, y, 1.3F, MagicText.getColor()); DrawableUtil.drawScaledText(context, ExperienceGroup.forLevel( currentScaledLevel, currentCorruption @@ -89,7 +89,7 @@ public class SpellbookProfilePageContent implements SpellbookChapterList.Content matrices.push(); matrices.translate(screen.getBackgroundWidth() / 2 + SpellbookScreen.TITLE_X - 10, y, 0); matrices.scale(1.3F, 1.3F, 1); - context.drawText(font, SpellbookCraftingPageContent.INVENTORY_TITLE, 0, 0, SpellbookScreen.TITLE_COLOR, false); + context.drawText(font, SpellbookCraftingPageContent.INVENTORY_TITLE, 0, 0, MagicText.getColor(), false); matrices.pop(); Bounds bounds = screen.getFrameBounds(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java index 8234fcff..e876ea92 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java @@ -159,7 +159,6 @@ public class SpellbookScreen extends HandledScreen imple tabs.getAllTabs().forEach(tab -> { Bounds bounds = tab.bounds(); - chapters.getCurrentChapter(); boolean hover = bounds.contains(mouseX, mouseY); int color = tab.chapter().color() & 0xFFFFFF; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java index 80812afb..cbcd7392 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java @@ -5,6 +5,7 @@ import java.util.*; import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.client.gui.MagicText; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Content; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Drawable; @@ -12,7 +13,6 @@ import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow; import com.minelittlepony.unicopia.container.SpellbookState; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.network.PacketByteBuf; @@ -21,13 +21,15 @@ import net.minecraft.util.*; public class DynamicContent implements Content { private static final Text UNKNOWN = Text.of("???"); - private static final Text UNKNOWN_LEVEL = Text.literal("Level: ???").formatted(Formatting.DARK_GREEN); private SpellbookState.PageState state = new SpellbookState.PageState(); private final List pages; private Bounds bounds = Bounds.empty(); + private final Panel leftPanel = new Panel(this); + private final Panel rightPanel = new Panel(this); + public DynamicContent(PacketByteBuf buffer) { pages = buffer.readList(Page::new); } @@ -35,32 +37,25 @@ public class DynamicContent implements Content { @Override public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) { int pageIndex = state.getOffset() * 2; + MinecraftClient client = MinecraftClient.getInstance(); - getPage(pageIndex).ifPresent(page -> page.draw(context, mouseX, mouseY, container)); - - context.getMatrices().push(); - getPage(pageIndex + 1).ifPresent(page -> { - page.bounds.left = bounds.left + bounds.width / 2 + 20; - page.draw(context, mouseX, mouseY, container); - }); - context.getMatrices().pop(); - - TextRenderer font = MinecraftClient.getInstance().textRenderer; - int headerColor = mouseY % 255; - - Text pageText = Text.translatable("%s/%s", (pageIndex / 2) + 1, pages.size() / 2); - context.drawText(font, pageText, (int)(337 - font.getWidth(pageText) / 2F), 190, headerColor, false); + Text pageText = Text.translatable("%s/%s", (pageIndex / 2) + 1, (int)Math.ceil(pages.size() / 2F)); + context.drawText(client.textRenderer, pageText, (int)(337 - client.textRenderer.getWidth(pageText) / 2F), 190, MagicText.getColor(), false); } @Override public void copyStateFrom(Content old) { if (old instanceof DynamicContent o) { + if (state.getOffset() == o.state.getOffset()) { + leftPanel.verticalScrollbar.getScrubber().scrollTo(o.leftPanel.verticalScrollbar.getScrubber().getPosition(), false); + rightPanel.verticalScrollbar.getScrubber().scrollTo(o.rightPanel.verticalScrollbar.getScrubber().getPosition(), false); + } state = o.state; setBounds(o.bounds); } } - private Optional getPage(int index) { + Optional getPage(int index) { if (index < 0 || index >= pages.size()) { return Optional.empty(); } @@ -71,9 +66,15 @@ public class DynamicContent implements Content { this.bounds = bounds; pages.forEach(page -> { page.reset(); + int oldHeight = page.bounds.height; page.bounds.copy(bounds); + page.bounds.left = 0; + page.bounds.top = 0; page.bounds.width /= 2; + page.bounds.height = oldHeight; }); + + leftPanel.setBounds(bounds); } @Override @@ -83,6 +84,10 @@ public class DynamicContent implements Content { screen.addPageButtons(187, 30, 350, incr -> { state.swap(incr, (int)Math.ceil(pages.size() / 2F)); }); + + int pageIndex = state.getOffset() * 2; + leftPanel.init(screen, pageIndex); + rightPanel.init(screen, pageIndex + 1); } class Page implements Drawable { @@ -131,6 +136,19 @@ public class DynamicContent implements Content { compiled = false; } + public void drawHeader(DrawContext context, int mouseX, int mouseY) { + + if (elements.isEmpty()) { + return; + } + boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level; + int x = bounds.left; + int y = bounds.top - 16; + + DrawableUtil.drawScaledText(context, needsMoreXp ? UNKNOWN : title, x, y, 1.3F, MagicText.getColor()); + DrawableUtil.drawScaledText(context, Text.translatable("gui.unicopia.spellbook.page.level_requirement", level < 0 ? "???" : "" + (level + 1)).formatted(Formatting.DARK_GREEN), x, y + 12, 0.8F, MagicText.getColor()); + } + @Override public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) { @@ -141,26 +159,24 @@ public class DynamicContent implements Content { if (!compiled) { compiled = true; int relativeY = 0; + int textHeight = 0; for (PageElement element : elements.stream().filter(PageElement::isInline).toList()) { element.compile(relativeY, container); relativeY += element.bounds().height; + if (element instanceof TextBlock) { + textHeight += element.bounds().height; + } } + bounds.height = textHeight == 0 ? 0 : relativeY; } - boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level; - - int headerColor = mouseY % 255; - MatrixStack matrices = context.getMatrices(); - DrawableUtil.drawScaledText(context, needsMoreXp ? UNKNOWN : title, bounds.left, bounds.top - 10, 1.3F, headerColor); - DrawableUtil.drawScaledText(context, level < 0 ? UNKNOWN_LEVEL : Text.literal("Level: " + (level + 1)).formatted(Formatting.DARK_GREEN), bounds.left, bounds.top - 10 + 12, 0.8F, headerColor); matrices.push(); - matrices.translate(bounds.left, bounds.top + 16, 0); + matrices.translate(0, -8, 0); elements.stream().filter(PageElement::isFloating).forEach(element -> { - Bounds bounds = element.bounds(); matrices.push(); - bounds.translate(matrices); + element.bounds().translate(matrices); element.draw(context, mouseX, mouseY, container); matrices.pop(); }); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java new file mode 100644 index 00000000..315e1f7b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java @@ -0,0 +1,78 @@ +package com.minelittlepony.unicopia.client.gui.spellbook.element; + +import java.util.Optional; + +import com.minelittlepony.common.client.gui.IViewRoot; +import com.minelittlepony.common.client.gui.ScrollContainer; +import com.minelittlepony.common.client.gui.dimension.Bounds; +import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; +import com.minelittlepony.unicopia.client.gui.spellbook.element.DynamicContent.Page; + +import net.minecraft.client.gui.DrawContext; + +class Panel extends ScrollContainer { + private final DynamicContent content; + + Panel(DynamicContent content) { + this.content = content; + } + + private Optional page = Optional.empty(); + + public void init(SpellbookScreen screen, int pageIndex) { + verticalScrollbar.layoutToEnd = true; + getContentPadding().top = 15; + page = content.getPage(pageIndex); + + int width = screen.getBackgroundWidth() / 2; + margin.top = screen.getY() + 35; + margin.bottom = screen.height - screen.getBackgroundHeight() - screen.getY() + 40; + margin.left = screen.getX() + 30; + + if (pageIndex % 2 == 1) { + margin.left += screen.getBackgroundWidth() / 2 - 20; + margin.right = screen.getX() + 20; + } else { + margin.right = screen.width - width - margin.left + 30; + } + init(() -> {}); + screen.addDrawable(this); + ((IViewRoot)screen).getChildElements().add(this); + } + + @Override + protected void renderContents(DrawContext context, int mouseX, int mouseY, float partialTicks) { + page.ifPresent(p -> { + int oldHeight = p.getBounds().height; + p.draw(context, mouseX, mouseY, this); + if (p.getBounds().height != oldHeight) { + verticalScrollbar.reposition(); + } + }); + super.renderContents(context, mouseX, mouseY, partialTicks); + } + + @Override + public Bounds getContentBounds() { + return page == null ? Bounds.empty() : page.map(page -> { + return new Bounds(0, 0, 1, page.getBounds().height); + }).orElse(Bounds.empty()); + } + + @Override + protected void drawBackground(DrawContext context, int mouseX, int mouseY, float partialTicks) { } + + @Override + protected void drawDecorations(DrawContext context, int mouseX, int mouseY, float partialTicks) { } + + @Override + protected void drawOverlays(DrawContext context, int mouseX, int mouseY, float partialTicks) { + context.getMatrices().push(); + this.getBounds().translate(context.getMatrices()); + page.ifPresent(p -> { + p.drawHeader(context, mouseX, mouseY); + }); + context.getMatrices().pop(); + super.drawOverlays(context, mouseX, mouseY, partialTicks); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Recipe.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Recipe.java index 96233442..d99492c7 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Recipe.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Recipe.java @@ -4,7 +4,6 @@ import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe; import com.minelittlepony.unicopia.client.gui.spellbook.IngredientTree; -import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.client.MinecraftClient; @@ -14,18 +13,14 @@ import net.minecraft.util.Identifier; record Recipe (DynamicContent.Page page, Identifier id, Bounds bounds) implements PageElement { @Override public void compile(int y, IViewRoot container) { - if (container instanceof SpellbookScreen book) { - bounds().left = book.getX(); - bounds().top = book.getY(); - } MinecraftClient.getInstance().world.getRecipeManager().get(id).map(RecipeEntry::value).ifPresent(recipe -> { if (recipe instanceof SpellbookRecipe spellRecipe) { boolean needsMoreXp = page.getLevel() < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < page.getLevel(); IngredientTree tree = new IngredientTree( - bounds().left + page().getBounds().left, - bounds().top + page().getBounds().top + y + 10, + bounds().left, + bounds().top + y - 10, page().getBounds().width - 20 ).obfuscateResult(needsMoreXp); spellRecipe.buildCraftingTree(tree); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Stack.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Stack.java index 1113d668..f1f8195d 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Stack.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Stack.java @@ -4,19 +4,13 @@ import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.unicopia.ability.magic.spell.crafting.IngredientWithSpell; import com.minelittlepony.unicopia.client.gui.spellbook.IngredientTree; -import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; record Stack (DynamicContent.Page page, IngredientWithSpell ingredient, Bounds bounds) implements PageElement { @Override public void compile(int y, IViewRoot container) { - int xx = 0, yy = 0; - if (container instanceof SpellbookScreen book) { - xx = book.getX(); - yy = book.getY(); - } IngredientTree tree = new IngredientTree( - bounds().left + xx + page().getBounds().left, - bounds().top + yy + page().getBounds().top + y + 10, + bounds().left + page().getBounds().left, + bounds().top + page().getBounds().top + y - 10, 30 ); tree.input(ingredient.getMatchingStacks()); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java index 79867f24..422996d7 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java @@ -6,6 +6,7 @@ import java.util.function.Supplier; import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; +import com.minelittlepony.unicopia.client.gui.MagicText; import com.minelittlepony.unicopia.client.gui.ParagraphWrappingVisitor; import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow; import com.minelittlepony.unicopia.entity.player.Pony; @@ -52,7 +53,7 @@ class TextBlock implements PageElement { MatrixStack matrices = context.getMatrices(); matrices.push(); wrappedText.forEach(line -> { - context.drawText(font, needsMoreXp ? line.text().copy().formatted(Formatting.OBFUSCATED) : line.text().copy(), line.x(), 0, 0, false); + context.drawText(font, needsMoreXp ? line.text().copy().formatted(Formatting.OBFUSCATED) : line.text().copy(), line.x(), 0, MagicText.getColor(), false); matrices.translate(0, font.fontHeight, 0); }); matrices.pop(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java index fffcade8..cb5a2c51 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java @@ -2,6 +2,8 @@ package com.minelittlepony.unicopia.client.particle; import org.joml.Vector3f; +import com.minelittlepony.unicopia.client.render.RenderUtil; + import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleTextureSheet; import net.minecraft.client.render.BufferBuilder; @@ -9,6 +11,7 @@ import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; public abstract class AbstractGeometryBasedParticle extends Particle { @@ -36,6 +39,25 @@ public abstract class AbstractGeometryBasedParticle extends Particle { te.draw(); } + protected final void renderQuad(MatrixStack matrices, Tessellator te, BufferBuilder buffer, RenderUtil.Vertex[] corners, float alpha, float tickDelta) { + int light = getBrightness(tickDelta); + buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); + for (RenderUtil.Vertex corner : corners) { + var position = corner.position(matrices.peek().getPositionMatrix()); + buffer.vertex(position.x, position.y, position.z).texture(corner.texture().x, corner.texture().y).color(red, green, blue, alpha).light(light).next(); + } + te.draw(); + } + + protected final void renderQuad(Tessellator te, BufferBuilder buffer, RenderUtil.Vertex[] corners, float alpha, float tickDelta) { + int light = getBrightness(tickDelta); + buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); + for (RenderUtil.Vertex corner : corners) { + buffer.vertex(corner.position().x, corner.position().y, corner.position().z).texture(corner.texture().x, corner.texture().y).color(red, green, blue, alpha).light(light).next(); + } + te.draw(); + } + protected final void renderQuad(VertexConsumer buffer, Vector3f[] corners, float alpha, float tickDelta) { int light = getBrightness(tickDelta); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/ClientBoundParticleSpawner.java b/src/main/java/com/minelittlepony/unicopia/client/particle/ClientBoundParticleSpawner.java new file mode 100644 index 00000000..4fdc8e3a --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/ClientBoundParticleSpawner.java @@ -0,0 +1,57 @@ +package com.minelittlepony.unicopia.client.particle; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.function.BooleanSupplier; + +import com.minelittlepony.unicopia.particle.ParticleSpawner; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.particle.Particle; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +/** + * A connection class for updating and persisting an attached particle effect. + */ +public class ClientBoundParticleSpawner implements ParticleSpawner { + private static final Map SPAWNED_PARTICLES = new HashMap<>(); + + private final UUID id; + private WeakReference attachment = new WeakReference<>(null); + + private final MinecraftClient client = MinecraftClient.getInstance(); + + public ClientBoundParticleSpawner(UUID id) { + this.id = id; + } + + @Override + public void addParticle(ParticleEffect effect, Vec3d pos, Vec3d vel) { + BooleanSupplier a = attachment.get(); + if ((a == null || !a.getAsBoolean())) { + SPAWNED_PARTICLES.values().removeIf(set -> !set.getAsBoolean()); + attachment = new WeakReference<>(SPAWNED_PARTICLES.computeIfAbsent(id, i -> { + return new Entry( + new WeakReference<>(client.world), + new WeakReference<>(client.particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z)) + ); + })); + } + } + + private record Entry (WeakReference world, WeakReference particle) implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + if (world.get() == null || world.get() != MinecraftClient.getInstance().world) { + return false; + } + + Particle particle = this.particle.get(); + return particle != null && particle.isAlive(); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/CloudsEscapingParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/CloudsEscapingParticle.java index 50792cfe..32ff15f7 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/CloudsEscapingParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/CloudsEscapingParticle.java @@ -30,7 +30,7 @@ public class CloudsEscapingParticle extends GroundPoundParticle { ); double columnHeight = 1 + age / 30; - new Sphere(true, columnHeight, 1, 1, 1) + new Sphere(true, columnHeight) .translate(center) .randomPoints(random) .forEach(point -> { diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java new file mode 100644 index 00000000..e39bb0b0 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java @@ -0,0 +1,86 @@ +package com.minelittlepony.unicopia.client.particle; + +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.client.render.RenderUtil; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.particle.BlockStateParticleEffect; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; + +public class DustCloudParticle extends AbstractBillboardParticle { + //private static final Identifier TEXTURE = new Identifier("textures/particle/big_smoke_3.png"); + + protected static final int SEGMENTS = 20; + protected static final int SEPARATION = 270 / SEGMENTS; + + private float scaleFactor; + + protected Sprite sprite; + private final RenderUtil.Vertex[] vertices; + + public DustCloudParticle(BlockStateParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { + super(world, x, y, z, velocityX, velocityY, velocityZ); + maxAge = 1000; + gravityStrength = 1; + red = 0.6F; + green = 0.6F; + blue = 0.6F; + alpha = (float)world.getRandom().nextTriangular(0.6, 0.2); + scaleFactor = (float)world.getRandom().nextTriangular(2, 1.2); + sprite = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelParticleSprite(effect.getBlockState()); + vertices = new RenderUtil.Vertex[]{ + new RenderUtil.Vertex(-1, -1, 0, sprite.getMinU(), sprite.getMinV()), + new RenderUtil.Vertex(-1, 1, 0, sprite.getMaxU(), sprite.getMinV()), + new RenderUtil.Vertex( 1, 1, 0, sprite.getMaxU(), sprite.getMaxV()), + new RenderUtil.Vertex( 1, -1, 0, sprite.getMinU(), sprite.getMaxV()) + }; + if (!effect.getBlockState().isOf(Blocks.GRASS_BLOCK)) { + int i = MinecraftClient.getInstance().getBlockColors().getColor(effect.getBlockState(), world, BlockPos.ofFloored(x, y, z), 0); + red *= Color.r(i); + green *= Color.g(i); + blue *= Color.b(i); + } + } + + @Override + protected Identifier getTexture() { + return sprite.getAtlasId(); + } + + @Override + public void tick() { + super.tick(); + scaleFactor += 0.001F; + scale(MathHelper.clamp(age / 5F, 0, 1) * scaleFactor); + } + + @Override + protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) { + float scale = getScale(tickDelta); + float alpha = this.alpha * (1 - ((float)age / maxAge)); + MatrixStack matrices = new MatrixStack(); + matrices.translate(x, y, z); + matrices.scale(scale, scale * 0.5F, scale); + + float angle = ((this.age + tickDelta) % 360) / SEGMENTS; + + for (int i = 0; i < SEGMENTS; i++) { + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees((i * angle) % 360)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees((SEPARATION * i + angle) % 360)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((SEPARATION * i + angle) % 360)); + float ringScale = 1 + MathHelper.sin(((i * 10) + age + tickDelta) * 0.05F) * 0.1F; + matrices.scale(ringScale, ringScale, ringScale); + renderQuad(matrices, te, buffer, vertices, alpha, tickDelta); + matrices.pop(); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/FloatingBubbleParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/FloatingBubbleParticle.java new file mode 100644 index 00000000..68d51be7 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/FloatingBubbleParticle.java @@ -0,0 +1,31 @@ +package com.minelittlepony.unicopia.client.particle; + +import net.minecraft.client.particle.ParticleTextureSheet; +import net.minecraft.client.particle.SpriteBillboardParticle; +import net.minecraft.client.particle.SpriteProvider; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.particle.ParticleTypes; + +public class FloatingBubbleParticle extends SpriteBillboardParticle { + public FloatingBubbleParticle(ParticleEffect effect, SpriteProvider provider, ClientWorld clientWorld, double x, double y, double z, double dX, double dY, double dZ) { + super(clientWorld, x, y, z, dX, dY, dZ); + setSprite(provider); + scale((float)clientWorld.random.nextTriangular(1F, 0.5F)); + this.velocityX *= -0.1F; + this.velocityY *= -0.1F; + this.velocityZ *= -0.1F; + this.maxAge *= 3; + } + + @Override + public ParticleTextureSheet getType() { + return ParticleTextureSheet.PARTICLE_SHEET_OPAQUE; + } + + @Override + public void markDead() { + super.markDead(); + world.addParticle(ParticleTypes.BUBBLE_POP, x, y, z, 0, 0, 0); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/OrientedBillboardParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/OrientedBillboardParticle.java index f3f04b23..4c25b465 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/OrientedBillboardParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/OrientedBillboardParticle.java @@ -22,7 +22,6 @@ public abstract class OrientedBillboardParticle extends AbstractBillboardParticl fixed = effect.fixed(); if (fixed) { - // Was hamiltonianProduct (CHECK THIS!!) rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(effect.pitch())); rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - effect.yaw())); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java index 6093fa45..978fede1 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java @@ -1,39 +1,46 @@ package com.minelittlepony.unicopia.client.particle; -import java.util.ArrayList; import java.util.List; -import java.util.Optional; +import org.jetbrains.annotations.Nullable; -import org.joml.Vector3f; - -import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.client.render.bezier.BezierSegment; -import com.minelittlepony.unicopia.entity.player.Pony; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; -import com.minelittlepony.unicopia.particle.ParticleHandle.Link; +import com.minelittlepony.unicopia.client.render.bezier.Trail; +import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Tessellator; import net.minecraft.client.world.ClientWorld; -import net.minecraft.particle.DefaultParticleType; +import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; +import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; -public class RainbowTrailParticle extends AbstractBillboardParticle implements Attachment { +public class RainbowTrailParticle extends AbstractBillboardParticle { private static final Identifier TEXTURE = Unicopia.id("textures/particles/rainboom_trail.png"); - private final List segments = new ArrayList<>(); + private final Trail trail; - private Optional link = Optional.empty(); + @Nullable + private Entity target; + private boolean isAbility; - private boolean bound; - - public RainbowTrailParticle(DefaultParticleType effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { + public RainbowTrailParticle(TargetBoundParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { super(world, x, y, z, velocityX, velocityY, velocityZ); - segments.add(new Segment(new Vec3d(x, y, z))); + trail = new Trail(new Vec3d(x, y, z)); setMaxAge(300); + this.velocityX = velocityX; + this.velocityY = velocityY; + this.velocityZ = velocityZ; + + if (effect.getTargetId() <= 0) { + this.target = world.getOtherEntities(null, Box.from(trail.pos)).get(0); + } else { + this.target = world.getEntityById(effect.getTargetId()); + } + isAbility = Caster.of(target).filter(caster -> SpellType.RAINBOOM.isOn(caster)).isPresent(); } @Override @@ -42,98 +49,45 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A } @Override - public boolean isStillAlive() { - return age < getMaxAge() && (!dead || !segments.isEmpty()); - } - - @Override - public void attach(Link link) { - this.link = Optional.of(link); - bound = true; - } - - @Override - public void detach() { - link = Optional.empty(); - } - - @Override - public void setAttribute(int key, Number value) { - + public boolean isAlive() { + return age < getMaxAge() && (!dead || !trail.getSegments().isEmpty()); } @Override protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) { - float alpha = 1 - (float)age / maxAge; + float alpha = this.alpha * (1 - (float)age / maxAge); + + List segments = trail.getSegments(); for (int i = 0; i < segments.size() - 1; i++) { BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1)); float scale = getScale(tickDelta); corners.forEachCorner(corner -> { - corner.mul(scale); - corner.add(x, y, z); + corner.position().mul(scale).add(x, y, z); }); renderQuad(te, buffer, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta); } } - private void follow(EntityConvertable caster) { - Vec3d next = caster.asEntity().getPos(); - - if (segments.isEmpty()) { - segments.add(new Segment(next)); - } else { - Vec3d last = segments.get(segments.size() - 1).position; - if (next.distanceTo(last) > 0.2) { - segments.add(new Segment(next)); - } - } - } - @Override public void tick() { super.tick(); - if (link.isPresent()) { - age = 0; - link.flatMap(Link::get).ifPresent(this::follow); - } else if (!dead && !bound) { - follow(Pony.of(MinecraftClient.getInstance().player)); + if (target != null && target.isAlive()) { + if (isAbility) { + age = 0; + } + trail.update(target.getEyePos()); + + if (isAbility && Caster.of(target).filter(caster -> SpellType.RAINBOOM.isOn(caster)).isEmpty()) { + target = null; + } } - if (segments.size() > 1) { - segments.removeIf(Segment::tick); - } - if (segments.isEmpty()) { + if (trail.tick()) { markDead(); } } - - private final class Segment { - Vec3d position; - Vector3f offset; - - int age; - int maxAge; - - Segment(Vec3d position) { - this.position = position; - this.offset = new Vector3f((float)(position.getX() - x), (float)(position.getY() - y), (float)(position.getZ() - z)); - this.maxAge = 90; - } - - float getAlpha() { - return alpha * (1 - ((float)age / maxAge)); - } - - boolean tick() { - return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge; - } - - BezierSegment getPlane(Segment to) { - return new BezierSegment(offset, to.offset, 1); - } - } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java index 58a8d453..21294ca3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java @@ -1,26 +1,20 @@ package com.minelittlepony.unicopia.client.particle; -import java.util.Optional; - import org.joml.Quaternionf; import org.joml.Vector3f; -import com.minelittlepony.common.util.Color; -import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; -import com.minelittlepony.unicopia.particle.ParticleHandle.Link; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Tessellator; import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; import net.minecraft.util.math.*; -public class RunesParticle extends OrientedBillboardParticle implements Attachment { +@Deprecated +public class RunesParticle extends OrientedBillboardParticle { private static final Identifier[] TEXTURES = new Identifier[] { Unicopia.id("textures/particles/runes_0.png"), @@ -39,10 +33,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme private float prevRotationAngle; private float rotationAngle; - private Optional link = Optional.empty(); - - private int stasisAge = -1; - public RunesParticle(OrientedBillboardParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { super(effect, world, x, y, z, velocityX, velocityY, velocityZ); setMaxAge(70); @@ -52,52 +42,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme blue = world.random.nextFloat(); } - @Override - public boolean isStillAlive() { - return age < (maxAge - 1); - } - - @Override - public void attach(Link link) { - this.link = Optional.of(link); - velocityX = 0; - velocityY = 0; - velocityZ = 0; - Vec3d pos = link.get().map(EntityConvertable::asEntity).map(Entity::getPos).orElse(Vec3d.ZERO); - setPos(pos.x, pos.y + 0.25, pos.z); - } - - @Override - public void detach() { - link = Optional.empty(); - if (targetSize > 1) { - this.targetSize = 0; - } - } - - @Override - public void setAttribute(int key, Number value) { - if (key == ATTR_COLOR) { - int tint = value.intValue(); - red = Color.r(tint); - green = Color.g(tint); - blue = Color.b(tint); - } - if (key == ATTR_OPACITY) { - alpha = value.floatValue(); - } - if (key == ATTR_RADIUS) { - targetSize = value.floatValue(); - } - if (key == ATTR_PITCH) { - rotation = new Quaternionf(0, 0, 0, 1); - rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(value.floatValue())); - } - if (key == ATTR_YAW) { - rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - value.floatValue())); - } - } - @Override public float getScale(float tickDelta) { return MathHelper.lerp(tickDelta, prevBaseSize, baseSize) * super.getScale(tickDelta); @@ -165,15 +109,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme public void tick() { super.tick(); - link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> { - if (getAlphaScale() >= 0.9F) { - if (stasisAge < 0) { - stasisAge = age; - } - age = stasisAge; - } - }, this::detach); - prevBaseSize = baseSize; if (baseSize < targetSize) { baseSize += 0.1F; diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java index 911b10bd..7bbb555e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java @@ -10,22 +10,13 @@ import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; - -import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; import com.minelittlepony.unicopia.particle.SphereParticleEffect; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; -import com.minelittlepony.unicopia.particle.ParticleHandle.Link; import com.minelittlepony.unicopia.util.ColorHelper; import com.mojang.blaze3d.systems.RenderSystem; -import java.util.Optional; - -import com.minelittlepony.common.util.Color; - -public class SphereParticle extends Particle implements Attachment { +public class SphereParticle extends Particle { protected float prevRadius; protected float radius; @@ -34,12 +25,6 @@ public class SphereParticle extends Particle implements Attachment { protected float lerpIncrement; protected float toRadius; - private Optional link = Optional.empty(); - - private final SphereParticleEffect parameters; - - private boolean bound; - public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) { this(parameters, w, x, y, z); @@ -50,7 +35,6 @@ public class SphereParticle extends Particle implements Attachment { public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z) { super(w, x, y, z); - this.parameters = parameters; this.radius = parameters.radius(); this.red = parameters.color().x / 255F; this.green = parameters.color().y / 255F; @@ -60,43 +44,6 @@ public class SphereParticle extends Particle implements Attachment { setMaxAge(10); } - @Override - public boolean isStillAlive() { - return age < (maxAge - 1); - } - - @Override - public void attach(Link link) { - setMaxAge(50000); - this.link = Optional.of(link); - } - - @Override - public void detach() { - markDead(); - } - - @Override - public void setAttribute(int key, Number value) { - if (key == ATTR_RADIUS) { - toRadius = value.floatValue(); - steps = 20; - lerpIncrement = (toRadius - radius) / steps; - } - if (key == ATTR_COLOR) { - int tint = value.intValue(); - red = Color.r(tint); - green = Color.g(tint); - blue = Color.b(tint); - } - if (key == ATTR_OPACITY) { - alpha = value.floatValue(); - } - if (key == ATTR_BOUND) { - bound = value.intValue() == 1; - } - } - @Override public ParticleTextureSheet getType() { return ParticleTextureSheet.CUSTOM; @@ -106,24 +53,7 @@ public class SphereParticle extends Particle implements Attachment { public void tick() { super.tick(); - if (link.isPresent()) { - link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> { - if (!bound) { - Vec3d offset = parameters.offset(); - setPos(e.getX() + offset.getX(), e.getY() + offset.getY(), e.getZ() + offset.getZ()); - - prevPosX = e.lastRenderX + offset.getX(); - prevPosY = e.lastRenderY + offset.getY(); - prevPosZ = e.lastRenderZ + offset.getZ(); - } - }, this::detach); - - if (steps-- > 0) { - radius += lerpIncrement; - } - } else { - radius *= 0.9998281; - } + radius *= 0.9998281; } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java index 5ef86cf0..6b6cf861 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer; +import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate; import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher; import net.minecraft.client.MinecraftClient; @@ -14,17 +15,19 @@ import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.feature.FeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRendererContext; import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.LivingEntity; import net.minecraft.util.Arm; public class AccessoryFeatureRenderer< T extends LivingEntity, - M extends BipedEntityModel> extends FeatureRenderer { + M extends EntityModel> extends FeatureRenderer { - private static final List> REGISTRY = new ArrayList<>(); + private static final List> REGISTRY = new ArrayList<>(); - public static void register(FeatureFactory...factories) { + @SafeVarargs + public static void register(FeatureFactory>...factories) { for (var factory : factories) { REGISTRY.add(factory); } @@ -35,11 +38,15 @@ public class AccessoryFeatureRenderer< @SuppressWarnings("unchecked") public AccessoryFeatureRenderer(FeatureRendererContext context) { super(context); - features = REGISTRY.stream().map(f -> ((FeatureFactory)f).create(context)).toList(); + features = REGISTRY.stream().map(f -> ((FeatureFactory)f).create(context)).toList(); } @Override public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + if (MineLPDelegate.getInstance().getRace(entity).isEquine()) { + return; + } + features.forEach(feature -> feature.render(matrices, vertexConsumers, light, entity, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch)); Caster.of(entity).ifPresent(caster -> { @@ -52,6 +59,10 @@ public class AccessoryFeatureRenderer< } public boolean beforeRenderArms(ArmRenderer sender, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, int light) { + Caster caster = Caster.of(entity).orElse(null); + if (caster != null) { + SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, caster, 0, 0, tickDelta, entity.age + tickDelta, 0, 0); + } boolean cancelled = false; for (var feature : features) { cancelled |= feature.beforeRenderArms(sender, tickDelta, matrices, vertexConsumers, entity, light); @@ -59,8 +70,8 @@ public class AccessoryFeatureRenderer< return cancelled; } - public interface FeatureFactory { - Feature create(FeatureRendererContext> context); + public interface FeatureFactory> { + Feature create(FeatureRendererContext context); } public interface Feature { @@ -75,11 +86,11 @@ public class AccessoryFeatureRenderer< public interface FeatureRoot< T extends LivingEntity, - M extends BipedEntityModel> { + M extends EntityModel> { AccessoryFeatureRenderer getAccessories(); @SuppressWarnings("unchecked") @Nullable - static > FeatureRoot of(T entity) { + static > FeatureRoot of(T entity) { var renderer = MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity); if (renderer instanceof FeatureRoot) { return (FeatureRoot)renderer; diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java index 80eeefd4..45a3ae35 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java @@ -39,6 +39,15 @@ public final class RenderLayers extends RenderLayer { .target(TRANSLUCENT_TARGET) .build(false)); + private static final RenderLayer MAGIC_SHIELD = of("magic_shield", VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL, + VertexFormat.DrawMode.QUADS, 256, true, true, MultiPhaseParameters.builder() + .program(COLOR_PROGRAM) + .transparency(TRANSLUCENT_TRANSPARENCY) + .target(TRANSLUCENT_TARGET) + .cull(DISABLE_CULLING) + .writeMaskState(COLOR_MASK) + .build(false)); + private static final Function MAGIC_COLORIN_FUNC = Util.memoize(color -> { return of("magic_colored_" + color, VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL, @@ -75,6 +84,10 @@ public final class RenderLayers extends RenderLayer { return MAGIC_NO_COLOR; } + public static RenderLayer getMagicShield() { + return MAGIC_SHIELD; + } + public static RenderLayer getMagicColored() { return MAGIC_COLORED; } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java index a126ee28..cb43a128 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java @@ -11,31 +11,46 @@ import net.minecraft.client.render.VertexFormats; import net.minecraft.client.util.math.MatrixStack; public class RenderUtil { - private static final Vector4f TEMP_VECTOR = new Vector4f(); + public static final Vector4f TEMP_VECTOR = new Vector4f(); + private static final Vector4f TEMP_UV_VECTOR = new Vector4f(); public static final Vertex[] UNIT_FACE = new Vertex[] { - new Vertex(new Vector3f(0, 0, 0), 1, 1), - new Vertex(new Vector3f(0, 1, 0), 1, 0), - new Vertex(new Vector3f(1, 1, 0), 0, 0), - new Vertex(new Vector3f(1, 0, 0), 0, 1) + new Vertex(0, 0, 0, 1, 1), + new Vertex(0, 1, 0, 1, 0), + new Vertex(1, 1, 0, 0, 0), + new Vertex(1, 0, 0, 0, 1) + }; + public static final Vertex[] FRAME_BUFFER_VERTICES = new Vertex[] { + new Vertex(0, 1, 0, 0, 0), + new Vertex(1, 1, 0, 1, 0), + new Vertex(1, 0, 0, 1, 1), + new Vertex(0, 0, 0, 0, 1) }; public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) { + renderFace(matrices, te, buffer, r, g, b, a, light, 1, 1); + } + + public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light, float uScale, float vScale) { buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); - - Vertex[] UNIT_FACE = new Vertex[] { - new Vertex(new Vector3f(0, 0, 0), 1, 1), - new Vertex(new Vector3f(0, 1, 0), 1, 0), - new Vertex(new Vector3f(1, 1, 0), 0, 0), - new Vertex(new Vector3f(1, 0, 0), 0, 1) - }; - - Matrix4f transformation = matrices.peek().getPositionMatrix(); + Matrix4f positionmatrix = matrices.peek().getPositionMatrix(); for (Vertex vertex : UNIT_FACE) { - transformation.transform(TEMP_VECTOR.set(vertex.position(), 1)); - buffer.vertex(TEMP_VECTOR.x, TEMP_VECTOR.y, TEMP_VECTOR.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next(); + Vector4f position = vertex.position(positionmatrix); + buffer.vertex(position.x, position.y, position.z).texture(vertex.texture().x * uScale, vertex.texture().y * vScale).color(r, g, b, a).light(light).next(); } te.draw(); } - record Vertex(Vector3f position, float u, float v) {} + public record Vertex(Vector3f position, Vector3f texture) { + public Vertex(float x, float y, float z, float u, float v) { + this(new Vector3f(x, y, z), new Vector3f(u, v, 1)); + } + + public Vector4f position(Matrix4f mat) { + return mat.transform(TEMP_VECTOR.set(position, 1)); + } + + public Vector4f texture(Matrix4f mat) { + return mat.transform(TEMP_UV_VECTOR.set(texture, 1)); + } + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java index f38b0182..bb30b1f5 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java @@ -4,20 +4,22 @@ import java.util.function.Consumer; import org.joml.Vector3f; +import com.minelittlepony.unicopia.client.render.RenderUtil; + public record BezierSegment( - Vector3f[] corners + RenderUtil.Vertex[] corners ) { public BezierSegment(Vector3f from, Vector3f to, float height) { - this(new Vector3f[] { - new Vector3f(from.x, from.y - height/2F, from.z), // bottom left - new Vector3f(from.x, from.y + height/2F, from.z), // top left - new Vector3f(to.x, to.y + height/2F, to.z), // top right - new Vector3f(to.x, to.y - height/2F, to.z) // bottom right + this(new RenderUtil.Vertex[] { + new RenderUtil.Vertex(from.x, from.y - height/2F, from.z, 0, 0), // bottom left + new RenderUtil.Vertex(from.x, from.y + height/2F, from.z, 1, 0), // top left + new RenderUtil.Vertex(to.x, to.y + height/2F, to.z, 1, 1), // top right + new RenderUtil.Vertex(to.x, to.y - height/2F, to.z, 0, 1) // bottom right }); } - public void forEachCorner(Consumer transformer) { + public void forEachCorner(Consumer transformer) { for (var corner : corners) { transformer.accept(corner); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java deleted file mode 100644 index 361a437a..00000000 --- a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.minelittlepony.unicopia.client.render.bezier; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import org.joml.Vector3f; - -import net.minecraft.util.math.Vec3d; - -public class Ribbon implements Iterable { - public float width; - public float angle; - - private final List nodes = new ArrayList<>(); - private final List segments = new ArrayList<>(); - private Node lastNode; - - public Ribbon(Vec3d position, Vector3f bottom, Vector3f top, float angle) { - this.angle = angle; - lastNode = new Node(position, position.toVector3f().add(bottom), position.toVector3f().add(top)); - nodes.add(lastNode); - } - - public void addNode(Vec3d position, float angle) { - Vector3f directionVector = position.subtract(lastNode.position()).toVector3f(); - - Vector3f bottom = lastNode.bottom().add(directionVector).rotateAxis(angle, directionVector.x, directionVector.y, directionVector.z); - Vector3f top = lastNode.top().add(directionVector).rotateAxis(angle, directionVector.x, directionVector.y, directionVector.z); - - Node oldNode = lastNode; - lastNode = new Node(position, bottom, top); - nodes.add(lastNode); - segments.add(new BezierSegment(new Vector3f[] { - oldNode.bottom(), - oldNode.top(), - lastNode.bottom(), - lastNode.top() - })); - } - - @Override - public Iterator iterator() { - return segments.iterator(); - } - - record Node(Vec3d position, Vector3f bottom, Vector3f top) { - - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java new file mode 100644 index 00000000..f130fbcc --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java @@ -0,0 +1,67 @@ +package com.minelittlepony.unicopia.client.render.bezier; + +import java.util.ArrayList; +import java.util.List; +import org.joml.Vector3f; + +import net.minecraft.util.math.Vec3d; + +public class Trail { + + private final List segments = new ArrayList<>(); + + public final Vec3d pos; + + public Trail(Vec3d pos) { + this.pos = pos; + segments.add(new Segment(pos)); + } + + public List getSegments() { + return segments; + } + + public void update(Vec3d newPosition) { + if (segments.isEmpty()) { + segments.add(new Segment(newPosition)); + } else { + Vec3d last = segments.get(segments.size() - 1).position; + if (newPosition.distanceTo(last) > 0.2) { + segments.add(new Segment(newPosition)); + } + } + } + + public boolean tick() { + if (segments.size() > 1) { + segments.removeIf(Segment::tick); + } + return segments.isEmpty(); + } + + public final class Segment { + Vec3d position; + Vector3f offset; + + int age; + int maxAge; + + Segment(Vec3d position) { + this.position = position; + this.offset = position.subtract(pos).toVector3f(); + this.maxAge = 90; + } + + public float getAlpha() { + return (1 - ((float)age / maxAge)); + } + + boolean tick() { + return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge; + } + + public BezierSegment getPlane(Segment to) { + return new BezierSegment(offset, to.offset, 1); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java index 7b4fae4e..ece6638b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java @@ -11,8 +11,6 @@ import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.util.Identifier; public class CastSpellEntityRenderer extends EntityRenderer { - private final SpellEffectsRenderDispatcher spellRenderDispatcher = new SpellEffectsRenderDispatcher(); - public CastSpellEntityRenderer(EntityRendererFactory.Context ctx) { super(ctx); } @@ -24,7 +22,7 @@ public class CastSpellEntityRenderer extends EntityRenderer { @Override public void render(CastSpellEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) { - spellRenderDispatcher.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0); + SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0); } protected float getAnimationProgress(CastSpellEntity entity, float tickDelta) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java new file mode 100644 index 00000000..b8bb4c20 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java @@ -0,0 +1,85 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import com.minelittlepony.unicopia.entity.mob.IgnominiousBulbEntity; + +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.entity.model.EntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; + +public class IgnominiousBulbEntityModel extends EntityModel { + + private final ModelPart part; + + private final ModelPart head; + private final ModelPart leaves; + + public IgnominiousBulbEntityModel(ModelPart root) { + super(RenderLayer::getEntityTranslucent); + this.part = root; + head = root.getChild("head"); + leaves = root.getChild("leaves"); + } + + public static TexturedModelData getTexturedModelData() { + ModelData data = new ModelData(); + ModelPartData root = data.getRoot(); + ModelPartData head = root.addChild("head", ModelPartBuilder.create().uv(0, 0).cuboid(-24.0432F, -0.9905F, -48.1305F, 48, 23, 48, Dilation.NONE), ModelTransform.of(0, 7, 24, -0.1308F, 0.0057F, 0.0433F)); + head.addChild("jaw", ModelPartBuilder.create().uv(112, 0).cuboid(-16, -27, 5, 32, 0, 32, Dilation.NONE), ModelTransform.of(1.3731F, -21.6152F, -11.61F, -1.9802F, 0.1003F, 0.0006F)); + head.addChild("head", ModelPartBuilder.create().uv(0, 71).cuboid(-23, -16, -46, 46, 23, 46, Dilation.NONE), ModelTransform.of(-0.0432F, -0.9905F, -0.1305F, -0.1309F, 0, 0)); + + ModelPartData leaves = root.addChild("leaves", ModelPartBuilder.create(), ModelTransform.pivot(0, 27, -1)); + leaves.addChild("leaf_1", ModelPartBuilder.create().uv(112, 0).cuboid(-16, 3, -55, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(2.8316F, -1.0127F, -3.0858F)); + leaves.addChild("leaf_2", ModelPartBuilder.create().uv(112, 0).cuboid(-14, 12, -60, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(2.6063F, -0.075F, -3.1196F)); + leaves.addChild("leaf_3", ModelPartBuilder.create().uv(112, 0).cuboid(-20, 1, -51, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-0.7079F, -1.4622F, 0.4842F)); + leaves.addChild("leaf_4", ModelPartBuilder.create().uv(112, 0).cuboid(-16, 6, -58, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-0.4967F, 0.1003F, 0.0006F)); + leaves.addChild("leaf_5", ModelPartBuilder.create().uv(-64, 140).cuboid(-30, 7, -83, 64, 0, 64, Dilation.NONE), ModelTransform.rotation(2.8708F, -0.1388F, -3.0651F)); + leaves.addChild("leaf_6", ModelPartBuilder.create().uv(-64, 140).cuboid(-30, 7, -83, 64, 0, 64, Dilation.NONE), ModelTransform.rotation(-1.084F, -1.4961F, 0.807F)); + leaves.addChild("leaf_7", ModelPartBuilder.create().uv(-64, 140).cuboid(-30, 3, -83, 64, 0, 64, Dilation.NONE), ModelTransform.rotation(-0.2651F, -0.035F, -0.0332F)); + leaves.addChild("leaf_8", ModelPartBuilder.create().uv(-64, 140).cuboid(-30, 7, -83, 64, 0, 64, Dilation.NONE), ModelTransform.rotation(-1.95F, 1.4932F, -1.6857F)); + leaves.addChild("leaf_9", ModelPartBuilder.create().uv(112, 0).cuboid(-16, 10, -57, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-2.5197F, 1.466F, -2.0202F)); + leaves.addChild("leaf_10", ModelPartBuilder.create().uv(112, 0).cuboid(-8, 3, -61, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(2.9628F, 0.6133F, -2.978F)); + leaves.addChild("leaf_11", ModelPartBuilder.create().uv(112, 0).cuboid(-13, 6, -60, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-0.4768F, 0.8786F, -0.1186F)); + leaves.addChild("leaf_12", ModelPartBuilder.create().uv(112, 0).cuboid(-16, 0, -59, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-0.2911F, -0.5857F, 0.0605F)); + + return TexturedModelData.of(data, 256, 256); + } + + @Override + public void setAngles(IgnominiousBulbEntity entity, float limbSwing, float limbSwingAmount, float tickDelta, float yaw, float pitch) { + + float age = entity.age + tickDelta; + float scale = entity.getScale(tickDelta); + + part.xScale = scale; + part.yScale = scale; + part.zScale = scale; + part.pivotY = (1 - scale) * 24; + part.yaw = yaw * MathHelper.RADIANS_PER_DEGREE; + + head.yScale = 1 - MathHelper.sin(age * 0.05F) * 0.02F; + + float hScale = 1 + MathHelper.cos(age * 0.06F) * 0.02F; + head.xScale = hScale; + head.zScale = hScale; + + head.pitch = MathHelper.sin(age * 0.02F) * 0.02F; + head.yaw = (MathHelper.cos(age * 0.02F) * 0.02F); + + leaves.yScale = 1 + MathHelper.sin(age * 0.05F) * 0.12F; + leaves.yaw = -part.yaw; + } + + @Override + public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) { + part.render(matrices, vertices, light, overlay); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityRenderer.java new file mode 100644 index 00000000..72c6b5d9 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityRenderer.java @@ -0,0 +1,39 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.mob.IgnominiousBulbEntity; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; + +public class IgnominiousBulbEntityRenderer extends EntityRenderer { + private static final Identifier IDLE_TEXTURE = Unicopia.id("textures/entity/poison_joke/bulb_idle.png"); + private static final Identifier ANGRY_TEXTURE = Unicopia.id("textures/entity/poison_joke/bulb_angry.png"); + + private final IgnominiousBulbEntityModel model; + + public IgnominiousBulbEntityRenderer(EntityRendererFactory.Context context) { + super(context); + model = new IgnominiousBulbEntityModel(IgnominiousBulbEntityModel.getTexturedModelData().createModel()); + } + + @Override + public void render(IgnominiousBulbEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { + matrices.push(); + matrices.scale(-1, -1, 1); + matrices.translate(0, -1.5F, 0); + + model.setAngles(entity, 0, 0, tickDelta, 180 + yaw, entity.getPitch(tickDelta)); + model.render(matrices, vertices.getBuffer(model.getLayer(getTexture(entity))), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); + matrices.pop(); + super.render(entity, yaw, tickDelta, matrices, vertices, light); + } + + @Override + public Identifier getTexture(IgnominiousBulbEntity entity) { + return entity.isAngry() ? ANGRY_TEXTURE : IDLE_TEXTURE; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/SpellbookEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/SpellbookEntityRenderer.java index f04a5494..ceac4e95 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/SpellbookEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/SpellbookEntityRenderer.java @@ -1,19 +1,31 @@ package com.minelittlepony.unicopia.client.render.entity; +import org.joml.Matrix3f; +import org.joml.Matrix4f; + import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.mob.SpellbookEntity; +import com.minelittlepony.unicopia.server.world.Altar; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.EntityRendererFactory; import net.minecraft.client.render.entity.LivingEntityRenderer; +import net.minecraft.client.render.entity.feature.FeatureRenderer; +import net.minecraft.client.render.entity.feature.FeatureRendererContext; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; import net.minecraft.util.math.*; public class SpellbookEntityRenderer extends LivingEntityRenderer { private static final Identifier TEXTURE = Unicopia.id("textures/entity/spellbook/normal.png"); + private static final Identifier ALTAR_BEAM_TEXTURE = new Identifier("textures/entity/end_crystal/end_crystal_beam.png"); public SpellbookEntityRenderer(EntityRendererFactory.Context context) { super(context, new SpellbookModel(SpellbookModel.getTexturedModelData().createModel()), 0); + addFeature(new AltarBeamFeature(this)); } @Override @@ -51,4 +63,79 @@ public class SpellbookEntityRenderer extends LivingEntityRenderer { + public AltarBeamFeature(FeatureRendererContext context) { + super(context); + } + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, int light, SpellbookEntity entity, float limbPos, float limbSpeed, float tickDelta, float animationProgress, float yaw, float pitch) { + if (!entity.hasBeams()) { + return; + } + + matrices.peek(); + matrices.pop(); + matrices.push(); + + + Altar altar = entity.getAltar().get(); + Vec3d center = altar.origin().toCenterPos(); + + float x = (float)MathHelper.lerp(tickDelta, entity.prevX, entity.getX()); + float y = (float)MathHelper.lerp(tickDelta, entity.prevY, entity.getY()); + float z = (float)MathHelper.lerp(tickDelta, entity.prevZ, entity.getZ()); + Vec3d bookPos = new Vec3d(x, y, z); + Vec3d shift = bookPos.subtract(center); + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180)); + matrices.translate(shift.x, shift.y - 1, shift.z); + + for (BlockPos pillar : altar.pillars()) { + renderBeam(center.subtract(pillar.toCenterPos()), -tickDelta, -entity.age, matrices, vertices, light, 1, 0, 1); + } + + matrices.pop(); + } + + public static float getYOffset(float animationProgress) { + animationProgress = MathHelper.sin(animationProgress * 0.2F) * 0.5F + 0.5F; + return ((animationProgress * animationProgress + animationProgress) * 0.4F) - 1.4F; + } + } + + public static void renderBeam(Vec3d offset, float tickDelta, int age, MatrixStack matrices, VertexConsumerProvider buffers, int light, float r, float g, float b) { + final float horizontalDistance = (float)offset.horizontalLength(); + final float distance = (float)offset.length(); + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Y.rotation((float)(-Math.atan2(offset.z, offset.x)) - 1.5707964f)); + matrices.multiply(RotationAxis.POSITIVE_X.rotation((float)(-Math.atan2(horizontalDistance, offset.y)) - 1.5707964f)); + VertexConsumer buffer = buffers.getBuffer(RenderLayer.getEntityTranslucent(ALTAR_BEAM_TEXTURE)); + final float minV = -(age + tickDelta) * 0.01f; + final float maxV = minV + (distance / 32F); + final int sides = 8; + final float diameter = 0.35F; + float segmentX = 0; + float segmentY = diameter; + float minU = 0; + MatrixStack.Entry entry = matrices.peek(); + Matrix4f positionMat = entry.getPositionMatrix(); + Matrix3f normalMat = entry.getNormalMatrix(); + + for (int i = 1; i <= sides; i++) { + float o = MathHelper.sin(i * MathHelper.TAU / sides) * diameter; + float p = MathHelper.cos(i * MathHelper.TAU / sides) * diameter; + float maxU = i / (float)sides; + buffer.vertex(positionMat, segmentX * 0.2F, segmentY * 0.2F, 0).color(0, 0, 0, 255).texture(minU, minV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next(); + buffer.vertex(positionMat, segmentX, segmentY, distance).color(r, g, b, 1).texture(minU, maxV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next(); + buffer.vertex(positionMat, o, p, distance).color(r, g, b, 1).texture(maxU, maxV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next(); + buffer.vertex(positionMat, o * 0.2F, p * 0.2F, 0).color(0, 0, 0, 255).texture(maxU, minV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next(); + segmentX = o; + segmentY = p; + minU = maxU; + } + matrices.pop(); + } } \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java new file mode 100644 index 00000000..05b3da1b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java @@ -0,0 +1,147 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.minelittlepony.unicopia.entity.mob.TentacleEntity; + +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.entity.model.EntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; + +public class TentacleEntityModel extends EntityModel { + private final ModelPart part; + + private final Map parts = new HashMap<>(); + private final List bones = new ArrayList<>(); + private final List brushes; + + private final ModelPart tip; + + public TentacleEntityModel(ModelPart root) { + super(RenderLayer::getEntityTranslucent); + this.part = root; + for (String key : List.of("bone_a", "bone_b", "bone_c", "bone_d", "bone_e", "bone_f", "bone_g", "bone_h")) { + parts.put(key, root = root.getChild(key)); + bones.add(root); + } + var bone_a = parts.get("bone_a"); + brushes = List.of( + bone_a.getChild("brush_1"), + bone_a.getChild("brush_2"), + bone_a.getChild("brush_3"), + bone_a.getChild("brush_4"), + parts.get("bone_c").getChild("flower_1"), + parts.get("bone_d").getChild("flower_2"), + parts.get("bone_f").getChild("flower_3")); + tip = parts.get("bone_h").getChild("tip"); + } + + public static TexturedModelData getTexturedModelData() { + ModelData data = new ModelData(); + data.getRoot() + .addChild("bone_a", ModelPartBuilder.create().uv(0, 0).cuboid(-7, -10, -7, 14, 16, 14, Dilation.NONE), ModelTransform.pivot(0, 24, 0)) + .addChild("bone_b", ModelPartBuilder.create().uv(0, 30).cuboid(-6, -18, -6, 12, 19, 12, Dilation.NONE), ModelTransform.of(0, -9, 0, -0.2618F, 0, 0)) + .addChild("bone_c", ModelPartBuilder.create().uv(48, 20).cuboid(-5, -23, -5, 10, 23, 10, Dilation.NONE), ModelTransform.of(0, -16, 0, 0, 0, 0)) + .addChild("bone_d", ModelPartBuilder.create().uv(40, 53).cuboid(-4, -23, -4, 8, 21, 8, Dilation.NONE), ModelTransform.of(0, -18, 0, -0.1745F, 0, 0)) + .addChild("bone_e", ModelPartBuilder.create().uv(0, 61).cuboid(-3, -25, -3, 6, 22, 6, Dilation.NONE), ModelTransform.of(0, -18, 0, 0.1745F, 0, 0)) + .addChild("bone_f", ModelPartBuilder.create().uv(72, 53).cuboid(-3, -17, -3, 6, 15, 6, Dilation.NONE), ModelTransform.of(0, -22, 0, 0.1745F, 0, 0)) + .addChild("bone_g", ModelPartBuilder.create().uv(56, 0).cuboid(-2.6F, -16, -2.6F, 5.2F, 15, 5.2F, Dilation.NONE), ModelTransform.of(0, -15, 0, 0.3054F, 0, 0)) + .addChild("bone_h", ModelPartBuilder.create().uv(24, 61).cuboid(-2.1F, -17, -2.1F, 4.2F, 15, 4.2F, Dilation.NONE), ModelTransform.of(0, -13, 0, 0.2618F, 0, 0)); + return TexturedModelData.of(addFlowers(data), 128, 128); + } + + private static ModelData addFlowers(ModelData data) { + ModelPartData bone_a = data.getRoot().getChild("bone_a"); + createFlowerSinglet(createFlowerSinglet(bone_a.addChild("brush_1", ModelPartBuilder.create(), ModelTransform.of(-3, 0.6703F, -7.7725F, 0.6103F, -0.0535F, -0.5864F))).addChild("bundle", ModelPartBuilder.create(), ModelTransform.rotation(0.6103F, -0.0535F, -0.5864F))); + createFlowerSinglet(createFlowerSinglet(bone_a.addChild("brush_2", ModelPartBuilder.create(), ModelTransform.of(-5, 0.6703F, 4.2275F, -1.2698F, 0.9678F, -1.7981F))).addChild("bundle", ModelPartBuilder.create(), ModelTransform.rotation(-1.2698F, 0.9678F, -1.7981F))); + createFlowerSinglet(createFlowerSinglet(bone_a.addChild("brush_3", ModelPartBuilder.create(), ModelTransform.of( 6, 0.6703F, 4.2275F, -2.4079F, 0.2344F, -2.5374F))).addChild("bundle", ModelPartBuilder.create(), ModelTransform.rotation(-2.4079F, 0.2344F, -2.5374F))); + createFlowerSinglet(createFlowerSinglet(bone_a.addChild("brush_4", ModelPartBuilder.create(), ModelTransform.of( 6, 0.6703F, -6.7725F, 1.5929F, -0.909F, -1.1179F))).addChild("bundle", ModelPartBuilder.create(), ModelTransform.rotation(2.9259F, -0.8201F, -2.1974F))); + + ModelPartData bone_c = bone_a.getChild("bone_b").getChild("bone_c"); + createFlowerSinglet(bone_c.addChild("flower_1", ModelPartBuilder.create(), ModelTransform.of( 4, -15.6242F, -3.3435F, 2.9259F, -0.8201F, -2.4592F))); + + ModelPartData bone_d = bone_c.getChild("bone_d"); + createFlowerSinglet(bone_d.addChild("flower_2", ModelPartBuilder.create(), ModelTransform.of(-2, -17.1355F, 3.0055F, -2.8606F, -0.5942F, 2.4482F))); + ModelPartData bone_f = bone_d.getChild("bone_e").getChild("bone_f"); + createFlowerSinglet(bone_f.addChild("flower_3", ModelPartBuilder.create(), ModelTransform.of(-2, -6.0251F, 0.478F, 2.7587F, 0.3479F, 2.5432F))); + + ModelPartData flower_12 = createFlowerBunch(bone_f.getChild("bone_g").getChild("bone_h").addChild("tip", ModelPartBuilder.create(), ModelTransform.of(0, -15.1462F, 0.6365F, 2.7587F, 0.3479F, 2.9795F))); + createFlowerBunch(flower_12.addChild("bundle_1", ModelPartBuilder.create(), ModelTransform.rotation( 2.7587F, 0.3479F, 2.5868F))); + createFlowerBunch(flower_12.addChild("bundle_2", ModelPartBuilder.create(), ModelTransform.rotation(-1.9412F, -1.0444F, 1.7406F))); + return data; + } + + private static ModelPartData createFlowerSinglet(ModelPartData parent) { + parent.addChild("flower_1", ModelPartBuilder.create().uv(86, 0).cuboid(-14, 0, 0, 14, 0, 14, Dilation.NONE), ModelTransform.rotation(1.5708F, 0, 0.7854F)); + parent.addChild("flower_2", ModelPartBuilder.create().uv(86, 0).cuboid(-14, 0, 0, 14, 0, 14, Dilation.NONE), ModelTransform.rotation(3.1416F, 0.7854F, 1.5708F)); + return parent; + } + + private static ModelPartData createFlowerBunch(ModelPartData parent) { + parent.addChild("flower_1", ModelPartBuilder.create().uv(86, 0).cuboid(-50.2096F, 2.6718F, -13.6647F, 14, 0, 14, Dilation.NONE), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 1.5708F, 0, 0.7854F)); + parent.addChild("flower_2", ModelPartBuilder.create().uv(86, 0).cuboid(-27.1617F, -35.2665F, 9.3832F, 14, 0, 14, Dilation.NONE), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 3.1416F, 0.7854F, 1.5708F)); + return parent; + } + + @Override + public void setAngles(TentacleEntity entity, float limbSwing, float limbSwingAmount, float tickDelta, float yaw, float pitch) { + + boolean growing = entity.getGrowth(tickDelta) < 1; + + float age = entity.age + tickDelta + (entity.getUuid().getMostSignificantBits() % 100); + float idleWaveTimer = entity.getAnimationTimer(tickDelta); + + float attackProgress = entity.isAttacking() ? Math.abs(MathHelper.sin(entity.getAttackProgress(tickDelta) * MathHelper.PI)) : 0; + float attackCurve = attackProgress * -0.5F; + float sweepDirection = 1; + + float bendIntentisty = 1 + entity.getAttackProgress(tickDelta) / 2F; + + part.yaw = (yaw * MathHelper.RADIANS_PER_DEGREE) + MathHelper.HALF_PI * attackProgress; + + for (ModelPart bone : bones) { + float idlePitch = MathHelper.sin(idleWaveTimer) * 0.0226F * bendIntentisty; + float idleYaw = MathHelper.cos(idleWaveTimer + 0.53F) * 0.07F; + float idleRoll = MathHelper.sin(idleWaveTimer * 0.2F) * 0.0226F * bendIntentisty; + idleWaveTimer += 1.5F; + bendIntentisty += 3F; + bone.resetTransform(); + + if (!growing) { + bone.pitch = MathHelper.lerp(attackProgress, idlePitch, bone.pitch + attackCurve); + bone.yaw = MathHelper.lerp(attackProgress, idleYaw, bone.yaw + sweepDirection * attackCurve); + bone.roll = MathHelper.lerp(attackProgress, idleRoll, bone.roll); + } + attackCurve *= 1.04F; + } + + float direction = 1; + + for (ModelPart brush : brushes) { + brush.resetTransform(); + brush.pitch += MathHelper.sin(age * 0.003F) * 0.03F; + brush.yaw += MathHelper.cos(age * 0.003F) * 0.1F * (direction *= -1); + } + + tip.resetTransform(); + tip.pitch += MathHelper.sin(age * 0.003F) * 0.3F; + tip.yaw += MathHelper.sin(age * 0.03F) * 0.3F; + } + + @Override + public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) { + part.render(matrices, vertices, light, overlay); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java new file mode 100644 index 00000000..7aa694b5 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java @@ -0,0 +1,44 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.mob.TentacleEntity; + +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; + +public class TentacleEntityRenderer extends EntityRenderer { + private static final Identifier TEXTURE = Unicopia.id("textures/entity/poison_joke/tentacle.png"); + + private final TentacleEntityModel model; + + public TentacleEntityRenderer(EntityRendererFactory.Context context) { + super(context); + model = new TentacleEntityModel(TentacleEntityModel.getTexturedModelData().createModel()); + } + + @Override + public void render(TentacleEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { + matrices.push(); + matrices.scale(-1, -1, 1); + float scale = entity.getGrowth(tickDelta); + + matrices.translate(0, -0.75F + 3F * (1 - scale), 0); + scale = MathHelper.clamp(scale, 0.5F, 1); + matrices.scale(scale, scale, scale); + + model.setAngles(entity, 0, 0, tickDelta, entity.getYaw(tickDelta), entity.getPitch(tickDelta)); + model.render(matrices, vertices.getBuffer(model.getLayer(getTexture(entity))), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); + matrices.pop(); + super.render(entity, yaw, tickDelta, matrices, vertices, light); + } + + @Override + public Identifier getTexture(TentacleEntity entity) { + return TEXTURE; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java index f868ff04..3bceca54 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java @@ -1,40 +1,70 @@ package com.minelittlepony.unicopia.client.render.model; -import java.util.ArrayList; import java.util.List; import org.joml.Matrix4f; import org.joml.Vector4f; +import com.minelittlepony.unicopia.client.render.RenderUtil; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.util.math.MatrixStack; public class BakedModel { - private static final Vector4f drawVert = new Vector4f(); + protected final List vertices = new ObjectArrayList<>(); - protected final List vertices = new ArrayList<>(); + private final Matrix4f textureMatrix = new Matrix4f(); + + public Matrix4f getTextureMatrix() { + return textureMatrix; + } + + public void scaleUV(float uScale, float vScale) { + getTextureMatrix().scale(uScale, vScale, 1); + } protected void addVertex(Vector4f vertex) { - addVertex(vertex.x, vertex.y, vertex.z, 0, 0); + addVertex(vertex.x, vertex.y, vertex.z, (vertex.x + 1) * 0.5F, (vertex.z + 1) * 0.5F); } protected void addVertex(float x, float y, float z, float u, float v) { - vertices.add(new Vertex(x, y, z, u, v)); + vertices.add(new RenderUtil.Vertex(x, y, z, u, v)); } - public final void render(MatrixStack matrices, VertexConsumer vertexWriter, int light, int overlay, float scale, float r, float g, float b, float a) { + public final void render(MatrixStack matrices, VertexConsumer buffer, int light, int overlay, float scale, float r, float g, float b, float a) { scale = Math.abs(scale); if (scale < 0.001F) { return; } - Matrix4f model = matrices.peek().getPositionMatrix(); - for (Vertex vertex : vertices) { - drawVert.set(vertex.x() * scale, vertex.y() * scale, vertex.z() * scale, 1); - drawVert.mul(model); - vertexWriter.vertex(drawVert.x, drawVert.y, drawVert.z, r, g, b, a, vertex.u(), vertex.v(), overlay, light, 0, 0, 0); + matrices.push(); + matrices.scale(scale, scale, scale); + Matrix4f positionmatrix = matrices.peek().getPositionMatrix(); + for (RenderUtil.Vertex vertex : vertices) { + Vector4f pos = vertex.position(positionmatrix); + Vector4f tex = vertex.texture(textureMatrix); + buffer.vertex(pos.x, pos.y, pos.z, r, g, b, a, tex.x, tex.y, overlay, light, 0, 0, 0); } + matrices.pop(); + textureMatrix.identity(); } - record Vertex(float x, float y, float z, float u, float v) {} + public final void render(MatrixStack matrices, VertexConsumer buffer, float scale, float r, float g, float b, float a) { + scale = Math.abs(scale); + if (scale < 0.001F) { + return; + } + + matrices.push(); + matrices.scale(scale, scale, scale); + Matrix4f positionmatrix = matrices.peek().getPositionMatrix(); + for (RenderUtil.Vertex vertex : vertices) { + Vector4f pos = vertex.position(positionmatrix); + Vector4f tex = vertex.texture(textureMatrix); + buffer.vertex(pos.x, pos.y, pos.z).texture(tex.x, tex.y).color(r, g, b, a).next(); + } + matrices.pop(); + textureMatrix.identity(); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/PlaneModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/PlaneModel.java new file mode 100644 index 00000000..bfbcfc24 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/PlaneModel.java @@ -0,0 +1,12 @@ +package com.minelittlepony.unicopia.client.render.model; + +public class PlaneModel extends BakedModel { + public static final PlaneModel INSTANCE = new PlaneModel(); + + private PlaneModel() { + addVertex(-1, -1, 0, 0, 0); + addVertex(-1, 1, 0, 1, 0); + addVertex( 1, 1, 0, 1, 1); + addVertex( 1, -1, 0, 0, 1); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/SphereModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/SphereModel.java index 1d94ae8a..f1202b56 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/model/SphereModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/SphereModel.java @@ -20,7 +20,7 @@ public class SphereModel extends BakedModel { compileVertices(azimuthRange, zenithIncrement, azimuthIncrement, this::addVertex); } - private static void compileVertices(double azimuthRange, double zenithIncrement, double azimuthIncrement, Consumer collector) { + static void compileVertices(double azimuthRange, double zenithIncrement, double azimuthIncrement, Consumer collector) { Vector4f vector = new Vector4f(); for (double zenith = 0; zenith < DrawableUtil.PI; zenith += zenithIncrement) { for (double azimuth = 0; azimuth < azimuthRange; azimuth += azimuthIncrement) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java b/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java new file mode 100644 index 00000000..fd86786d --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java @@ -0,0 +1,29 @@ +package com.minelittlepony.unicopia.client.render.shader; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.Unicopia; +import net.fabricmc.fabric.api.client.rendering.v1.CoreShaderRegistrationCallback; +import net.minecraft.client.gl.ShaderProgram; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; + +public final class UShaders { + @Nullable + private static Supplier renderTypePortalSurfaceProgram = register("rendertype_portal_surface", VertexFormats.POSITION_COLOR); + + public static ShaderProgram getRenderTypePortalSurfaceProgram() { + return renderTypePortalSurfaceProgram.get(); + } + + public static void bootstrap() { } + + static Supplier register(String name, VertexFormat format) { + AtomicReference holder = new AtomicReference<>(); + CoreShaderRegistrationCallback.EVENT.register(context -> context.register(Unicopia.id(name), format, holder::set)); + return holder::get; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java new file mode 100644 index 00000000..48f0dbb9 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java @@ -0,0 +1,43 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.BubbleSpell; +import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.model.SphereModel; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.RotationAxis; + +public class BubbleSpellRenderer extends SpellRenderer { + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, BubbleSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + + matrices.push(); + double height = caster.asEntity().getEyeY() - caster.getOriginVector().y; + matrices.translate(0, height * 0.5F, 0); + + float radius = spell.getRadius(tickDelta) * 0.7F; + + VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicNoColor()); + + Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity(); + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-45)); + matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(45 + cameraEntity.getYaw(tickDelta))); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-cameraEntity.getPitch(tickDelta))); + + new SphereModel(40, 40, DrawableUtil.PI * 0.25F).render(matrices, buffer, light, 0, radius - 0.1F, 0.9F, 0.9F, 1, 0.3F); + matrices.pop(); + + SphereModel.SPHERE.render(matrices, buffer, light, 0, radius, 0.9F, 0.9F, 1, 0.25F); + + matrices.pop(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java new file mode 100644 index 00000000..2c03bc11 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java @@ -0,0 +1,108 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell; +import com.minelittlepony.unicopia.ability.magic.spell.effect.DarkVortexSpell; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.model.PlaneModel; +import com.minelittlepony.unicopia.client.render.model.SphereModel; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; + +public class DarkVortexSpellRenderer extends SpellRenderer { + + private static final Identifier ACCRETION_DISK_TEXTURE = Unicopia.id("textures/spells/dark_vortex/accretion_disk.png"); + + private static float cameraDistortion; + + public static float getCameraDistortion() { + cameraDistortion *= 0.9F; + cameraDistortion = MathHelper.clamp(cameraDistortion, 0, 80); + return cameraDistortion; + } + + @Override + public boolean shouldRenderEffectPass(int pass) { + return pass < 2; + } + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, DarkVortexSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + + Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity(); + + float radius = (float)spell.getEventHorizonRadius(); + float absDistance = (float)cameraEntity.getEyePos().distanceTo(caster.getOriginVector().add(0, 2, 0)); + + matrices.push(); + matrices.translate(0, 2 + radius, 0); + + SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, Math.min(radius * 0.6F, absDistance * 0.1F), 0, 0, 0, 1); + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90)); + matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90 + cameraEntity.getYaw(tickDelta))); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-cameraEntity.getPitch(tickDelta))); + + matrices.scale(0.7F, 1, 1); + + float distance = 1F / MathHelper.clamp((absDistance / (radius * 4)), 0.0000001F, 1); + distance *= distance; + if (absDistance < radius * 4) { + cameraDistortion += distance; + } + + matrices.scale(distance, distance, distance); + + if (absDistance > radius) { + matrices.push(); + matrices.translate(0, -0.1F, 0); + for (int i = 0; i < 10; i++) { + matrices.scale(1, 1, 0.796F); + float brightness = i / 10F; + SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicNoColor()), light, 1, radius * (1 + (0.25F * i)) * 0.7F, brightness, brightness, brightness, 0.2F); + } + matrices.pop(); + } + + SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getEndPortal()), light, 1, radius * 0.5F, 1, 0.5F, 0, 1); + + if (radius > 0.3F && absDistance > radius) { + radius *= 3 + radius; + + matrices.scale(radius, radius, radius); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 168)); + + VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(ACCRETION_DISK_TEXTURE)); + + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); + + matrices.push(); + matrices.scale(0.5F, 0.5F, 0.5F); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(33)); + + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); + matrices.pop(); + + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(45)); + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); + } + matrices.pop(); + matrices.pop(); + } + + @Override + protected void renderCountdown(MatrixStack matrices, TimedSpell spell, float tickDelta) { + + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java new file mode 100644 index 00000000..fbdfce47 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java @@ -0,0 +1,97 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; +import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.client.render.model.PlaneModel; +import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; + +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.RotationAxis; + +public class PlacedSpellRenderer extends SpellRenderer { + private static final Identifier[] TEXTURES = new Identifier[] { + Unicopia.id("textures/particles/runes_0.png"), + Unicopia.id("textures/particles/runes_1.png"), + Unicopia.id("textures/particles/runes_2.png"), + Unicopia.id("textures/particles/runes_3.png"), + Unicopia.id("textures/particles/runes_4.png"), + Unicopia.id("textures/particles/runes_5.png") + }; + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + if (!(caster.asEntity() instanceof CastSpellEntity castSpell)) { + return; + } + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-castSpell.getYaw())); + + + for (Spell delegate : spell.getDelegates()) { + renderAmbientEffects(matrices, vertices, spell, delegate, caster, light, animationProgress, tickDelta); + + matrices.push(); + float height = caster.asEntity().getHeight(); + matrices.translate(0, (-spell.pitch / 90F) * height * 0.5F, 0); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-spell.pitch)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw)); + SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, spell.getScale(tickDelta), limbDistance, tickDelta, animationProgress, headYaw, headPitch); + matrices.pop(); + } + + matrices.pop(); + } + + protected void renderAmbientEffects(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Spell delegate, Caster caster, int light, float animationProgress, float tickDelta) { + matrices.push(); + matrices.translate(0, 0.001, 0); + + float height = caster.asEntity().getHeight(); + matrices.translate(0, (-spell.pitch / 90F) * height * 0.5F, 0); + + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-spell.pitch)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); + + float scale = spell.getScale(tickDelta) * 3; + matrices.scale(scale, scale, scale); + + float angle = (animationProgress / 9F) % 360; + + int color = delegate.getType().getColor(); + + float red = Color.r(color); + float green = Color.g(color); + float blue = Color.b(color); + + SpellRenderer renderer = SpellEffectsRenderDispatcher.INSTANCE.getRenderer(delegate); + + for (int i = 0; i < TEXTURES.length; i++) { + if (!renderer.shouldRenderEffectPass(i)) { + continue; + } + VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(TEXTURES[i])); + + for (int dim = 0; dim < 3; dim++) { + float ringSpeed = (i % 2 == 0 ? i : -1) * i; + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle * ringSpeed)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angle * ringSpeed * dim)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle * ringSpeed * dim)); + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, red, green, blue, scale / ((float)(dim * 3) + 1)); + matrices.pop(); + } + } + + matrices.pop(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java new file mode 100644 index 00000000..823220f5 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java @@ -0,0 +1,293 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix3f; +import org.joml.Matrix4f; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.model.SphereModel; +import com.minelittlepony.unicopia.client.render.shader.UShaders; +import com.minelittlepony.unicopia.entity.EntityReference; +import com.minelittlepony.unicopia.entity.mob.UEntities; +import com.minelittlepony.unicopia.mixin.client.MixinMinecraftClient; +import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.systems.VertexSorter; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.SimpleFramebuffer; +import net.minecraft.client.option.Perspective; +import net.minecraft.client.render.BackgroundRenderer; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.Frustum; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.util.Window; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.screen.PlayerScreenHandler; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +class PortalFrameBuffer implements AutoCloseable { + private static final LoadingCache CACHE = CacheBuilder.newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .removalListener(n -> n.getValue().close()) + .build(CacheLoader.from(PortalFrameBuffer::new)); + + private static int recursionCount; + + @Nullable + public static PortalFrameBuffer unpool(UUID id) { + try { + return CACHE.get(id); + } catch (ExecutionException e) { + return null; + } + } + + @Nullable + private SimpleFramebuffer framebuffer; + @Nullable + private SimpleFramebuffer backgroundBuffer; + @Nullable + private WorldRenderer renderer; + @Nullable + private ClientWorld world; + + private boolean closed; + + private final MinecraftClient client = MinecraftClient.getInstance(); + + private boolean pendingDraw; + + @Nullable + private Frustum frustum; + + PortalFrameBuffer(UUID id) { } + + public void draw(MatrixStack matrices, VertexConsumerProvider vertices) { + matrices.translate(0, -0.001, 0); + + if (!(closed || framebuffer == null)) { + RenderSystem.assertOnRenderThread(); + GlStateManager._colorMask(true, true, true, false); + GlStateManager._enableDepthTest(); + GlStateManager._disableCull(); + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + BufferBuilder buffer = tessellator.getBuffer(); + float uScale = (float)framebuffer.viewportWidth / (float)framebuffer.textureWidth; + float vScale = (float)framebuffer.viewportHeight / (float)framebuffer.textureHeight; + RenderSystem.setShader(UShaders::getRenderTypePortalSurfaceProgram); + //RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); + RenderSystem._setShaderTexture(0, framebuffer.getColorAttachment()); + buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); + SphereModel.DISK.scaleUV(uScale, vScale); + + RenderSystem.setTextureMatrix(SphereModel.DISK.getTextureMatrix()); + SphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1); + tessellator.draw(); + + client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); + GlStateManager._enableCull(); + GlStateManager._colorMask(true, true, true, true); + GlStateManager._depthMask(true); + } else { + Vec3d skyColor = client.world.getSkyColor(client.gameRenderer.getCamera().getPos(), client.getTickDelta()); + SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicShield()), 0, 0, 2, (float)skyColor.x, (float)skyColor.y, (float)skyColor.z, 1); + } + } + + public void build(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { + + long refreshRate = Unicopia.getConfig().fancyPortalRefreshRate.get(); + if (refreshRate > 0 && framebuffer != null && System.currentTimeMillis() % refreshRate != 0) { + return; + } + + if (pendingDraw && recursionCount > Math.max(0, Unicopia.getConfig().maxPortalRecursion.get())) { + innerBuild(spell, caster, target); + return; + } + + if (pendingDraw) { + return; + } + pendingDraw = true; + if (recursionCount > 0) { + innerBuild(spell, caster, target); + } else { + ((MixinMinecraftClient)client).getRenderTaskQueue().add(() -> innerBuild(spell, caster, target)); + } + } + + private void innerBuild(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { + synchronized (client) { + pendingDraw = false; + + if (recursionCount > 0) { + return; + } + recursionCount++; + + try { + if (closed || client.interactionManager == null) { + close(); + return; + } + + var fov = client.options.getFov(); + int originalFov = fov.getValue(); + fov.setValue(110); + + Camera camera = client.gameRenderer.getCamera(); + + Entity cameraEntity = UEntities.CAST_SPELL.create(caster.asWorld()); + Vec3d offset = new Vec3d(0, -0.2F, -0.2F).rotateY(-spell.getTargetYaw() * MathHelper.RADIANS_PER_DEGREE); + + float yaw = spell.getTargetYaw() + camera.getYaw() - spell.getYaw() + 180; + float pitch = spell.getTargetPitch() + (camera.getPitch() - spell.getPitch()) * 1.65F; + + cameraEntity.setPosition(target.pos().add(offset)); + cameraEntity.setPitch(pitch); + cameraEntity.setYaw(yaw); + + drawWorld(cameraEntity, 400, 400); + + fov.setValue(originalFov); + } finally { + recursionCount--; + } + } + } + + private void drawWorld(Entity cameraEntity, int width, int height) { + Entity oldCameraEntity = client.cameraEntity; + Window window = client.getWindow(); + + int i = window.getFramebufferWidth(); + int j = window.getFramebufferHeight(); + + width = i; + height = j; + + Perspective perspective = client.options.getPerspective(); + MatrixStack view = RenderSystem.getModelViewStack(); + + Matrix4f proj = RenderSystem.getProjectionMatrix(); + Matrix3f invView = RenderSystem.getInverseViewRotationMatrix(); + + int fbo = client.getFramebuffer().fbo; + Camera camera = client.gameRenderer.getCamera(); + + WorldRenderer globalRenderer = client.worldRenderer; + try { + client.cameraEntity = cameraEntity; + client.getFramebuffer().endWrite(); + + if (framebuffer == null) { + framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); + framebuffer.setClearColor(0, 0, 0, 0); + framebuffer.clear(MinecraftClient.IS_SYSTEM_MAC); + } + + view.push(); + view.loadIdentity(); + RenderSystem.applyModelViewMatrix(); + + window.setFramebufferWidth(width); + window.setFramebufferHeight(height); + client.getFramebuffer().fbo = framebuffer.fbo; + + client.options.setPerspective(Perspective.FIRST_PERSON); + + RenderSystem.clear(GlConst.GL_DEPTH_BUFFER_BIT | GlConst.GL_COLOR_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); + framebuffer.beginWrite(true); + BackgroundRenderer.clearFog(); + RenderSystem.enableCull(); + + if (renderer == null) { + renderer = new WorldRenderer(client, client.getEntityRenderDispatcher(), client.getBlockEntityRenderDispatcher(), client.getBufferBuilders()); + } + if (client.world != world) { + world = client.world; + renderer.setWorld(client.world); + } + ((MixinMinecraftClient)client).setWorldRenderer(renderer); + + renderer.scheduleBlockRenders((int)cameraEntity.getX() / 16, (int)cameraEntity.getY() / 16, (int)cameraEntity.getZ() / 16); + + client.gameRenderer.setRenderHand(false); + + client.gameRenderer.renderWorld(1, 0, new MatrixStack()); + + // Strip transparency + RenderSystem.colorMask(false, false, false, true); + RenderSystem.clearColor(1, 1, 1, 1); + RenderSystem.clear(GlConst.GL_COLOR_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); + RenderSystem.colorMask(true, true, true, true); + + framebuffer.endWrite(); + } finally { + ((MixinMinecraftClient)client).setWorldRenderer(globalRenderer); + + client.getFramebuffer().fbo = fbo; + client.getFramebuffer().beginWrite(true); + + view.pop(); + RenderSystem.applyModelViewMatrix(); + RenderSystem.setProjectionMatrix(proj, VertexSorter.BY_Z); + RenderSystem.setInverseViewRotationMatrix(invView); + + window.setFramebufferWidth(i); + window.setFramebufferHeight(j); + + client.options.setPerspective(perspective); + client.cameraEntity = oldCameraEntity; + + if (recursionCount <= 1) { + client.gameRenderer.setRenderHand(true); + camera.update(client.world, + client.getCameraEntity() == null ? client.player : client.getCameraEntity(), + perspective.isFirstPerson(), + perspective.isFrontView(), + 1 + ); + } + } + } + + @Override + public void close() { + closed = true; + if (framebuffer != null) { + SimpleFramebuffer fb = framebuffer; + framebuffer = null; + fb.delete(); + } + if (backgroundBuffer != null) { + SimpleFramebuffer fb = backgroundBuffer; + backgroundBuffer = null; + fb.delete(); + } + if (renderer != null) { + renderer.close(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java new file mode 100644 index 00000000..a75c3f72 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java @@ -0,0 +1,81 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.model.SphereModel; +import com.minelittlepony.unicopia.entity.EntityReference; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.RotationAxis; + +public class PortalSpellRenderer extends SpellRenderer { + @Override + public boolean shouldRenderEffectPass(int pass) { + return pass == 0; + } + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, PortalSpell spell, Caster caster, int light, float strength, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + super.render(matrices, vertices, spell, caster, light, strength, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + + VertexConsumer buff = vertices.getBuffer(RenderLayers.getEndGateway()); + + matrices.push(); + matrices.translate(0, 0.02, 0); + SphereModel.DISK.render(matrices, buff, light, 0, 2F * strength, 1, 1, 1, 1); + matrices.pop(); + + if (Unicopia.getConfig().simplifiedPortals.get() || !spell.isLinked()) { + matrices.push(); + matrices.translate(0, -0.02, 0); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); + SphereModel.DISK.render(matrices, buff, light, 0, 2F * strength, 1, 1, 1, 1); + matrices.pop(); + return; + } + + // Fancy portal rendering is disabled for now + // Need to fix: + // 1. Transparent parts of the sky (because the game sets the clear to (0,0,0,0) + + if (caster.asEntity().distanceTo(client.cameraEntity) > 50) { + return; // don't bother rendering if too far away + } + if (client.cameraEntity == caster.asEntity()) { + return; + } + + matrices.push(); + matrices.scale(strength, strength, strength); + + spell.getTarget().ifPresent(target -> { + float grown = Math.min(caster.asEntity().age, 20) / 20F; + matrices.push(); + matrices.translate(0, -0.01, 0); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-spell.getYaw())); + matrices.scale(grown, 1, grown); + boolean inRange = MinecraftClient.getInstance().player.getPos().distanceTo(target.pos()) < MinecraftClient.getInstance().gameRenderer.getViewDistance(); + + PortalFrameBuffer buffer = PortalFrameBuffer.unpool(target.uuid()); + if (buffer != null) { + if (inRange) { + buffer.build(spell, caster, target); + } + buffer.draw(matrices, vertices); + } + if (!inRange) { + buffer = PortalFrameBuffer.unpool(caster.asEntity().getUuid()); + if (buffer != null) { + buffer.build(spell, caster, new EntityReference.EntityValues<>(caster.asEntity())); + } + } + matrices.pop(); + }); + + matrices.pop(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/RainboomSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/RainboomSpellRenderer.java new file mode 100644 index 00000000..0f7e0b9c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/RainboomSpellRenderer.java @@ -0,0 +1,13 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.RainboomAbilitySpell; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; + +public class RainboomSpellRenderer extends SpellRenderer { + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, RainboomAbilitySpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java new file mode 100644 index 00000000..260b322c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java @@ -0,0 +1,53 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.ShieldSpell; +import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.model.SphereModel; +import com.minelittlepony.unicopia.util.ColorHelper; + +import net.minecraft.client.option.Perspective; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; + +public class ShieldSpellRenderer extends SpellRenderer { + private final SphereModel model = new SphereModel(40, 40, DrawableUtil.PI); + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, ShieldSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + + matrices.push(); + double height = caster.asEntity().getEyeY() - caster.getOriginVector().y; + matrices.translate(0, height, 0); + + int color = ColorHelper.lerp(caster.getCorruption().getScaled(1) * (tickDelta / (1 + caster.asWorld().random.nextFloat())), spell.getType().getColor(), 0xFF000); + float[] colors = ColorHelper.changeSaturation(Color.r(color), Color.g(color), Color.b(color), 4); + float radius = 0.35F + spell.getRadius(tickDelta) + MathHelper.sin(animationProgress / 30F) * 0.01F; + + VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicShield()); + + boolean firstPerson = caster.asEntity() == client.player && client.options.getPerspective() == Perspective.FIRST_PERSON; + + float thickness = 0.02F * MathHelper.sin(animationProgress / 30F); + float alpha = 1 - Math.abs(MathHelper.sin(animationProgress / 20F)) * 0.2F; + + if (firstPerson) { + matrices.translate(0, -1.75F, 0); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(client.cameraEntity.getPitch(tickDelta))); + model.render(matrices, buffer, light, 1, radius, colors[0], colors[1], colors[2], alpha * 0.2F); + } else { + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); + model.render(matrices, buffer, light, 1, radius + thickness, colors[0], colors[1], colors[2], alpha * 0.08F); + model.render(matrices, buffer, light, 1, radius - thickness, colors[0], colors[1], colors[2], alpha * 0.05F); + model.render(matrices, buffer, light, 1, radius + thickness * 2, colors[0], colors[1], colors[2], alpha * 0.05F); + } + + matrices.pop(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index 0f2a7ef3..37c4c51b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -14,10 +14,10 @@ import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.Living; - import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer.TextLayerType; +import net.minecraft.client.option.Perspective; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; @@ -27,6 +27,7 @@ import net.minecraft.registry.Registries; import net.minecraft.resource.ResourceManager; import net.minecraft.resource.SynchronousResourceReloader; import net.minecraft.text.Text; +import net.minecraft.util.Colors; import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; import net.minecraft.util.math.Box; @@ -42,10 +43,21 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader REGISTRY.put(type, rendererFactory); } + static { + register(SpellType.PLACED_SPELL, PlacedSpellRenderer::new); + register(SpellType.SHIELD, ShieldSpellRenderer::new); + register(SpellType.DARK_VORTEX, DarkVortexSpellRenderer::new); + register(SpellType.BUBBLE, BubbleSpellRenderer::new); + register(SpellType.PORTAL, PortalSpellRenderer::new); + register(SpellType.RAINBOOM, RainboomSpellRenderer::new); + } + @Nullable private Map, SpellRenderer> renderers = Map.of(); private final MinecraftClient client = MinecraftClient.getInstance(); + private SpellEffectsRenderDispatcher() {} + @Override public Identifier getFabricId() { return ID; @@ -53,19 +65,28 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader @SuppressWarnings("unchecked") public SpellRenderer getRenderer(S spell) { - return (SpellRenderer)renderers.get(spell.getType()); + return (SpellRenderer)renderers.getOrDefault(spell.getType(), SpellRenderer.DEFAULT); + } + + public void render(MatrixStack matrices, VertexConsumerProvider vertices, Spell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + var renderer = getRenderer(spell); + + if (renderer != SpellRenderer.DEFAULT) { + client.getBufferBuilders().getEntityVertexConsumers().draw(); + + renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + } } public void render(MatrixStack matrices, VertexConsumerProvider vertices, int light, Caster caster, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { caster.getSpellSlot().forEach(spell -> { - var renderer = getRenderer(spell); - if (renderer != null) { - renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); - } + render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); return Operation.SKIP; }, false); - if (client.getEntityRenderDispatcher().shouldRenderHitboxes() && !client.hasReducedDebugInfo()) { + if (client.getEntityRenderDispatcher().shouldRenderHitboxes() + && !client.hasReducedDebugInfo() + && !(caster.asEntity() == client.cameraEntity && client.options.getPerspective() == Perspective.FIRST_PERSON)) { renderHotspot(matrices, vertices, caster, animationProgress); renderSpellDebugInfo(matrices, vertices, caster, light); } @@ -109,7 +130,7 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader int left = (int)caster.asEntity().getWidth() * 64; for (Text line : debugLines) { - client.textRenderer.draw(line, left += 1, top += spacing, 0xFFFFFFFF, false, matrices.peek().getPositionMatrix(), vertices, TextLayerType.NORMAL, j, light); + client.textRenderer.draw(line, left += 1, top += spacing, Colors.WHITE, false, matrices.peek().getPositionMatrix(), vertices, TextLayerType.NORMAL, j, light); } matrices.pop(); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellRenderer.java index 8c7e6466..cdaf9f79 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellRenderer.java @@ -1,11 +1,73 @@ package com.minelittlepony.unicopia.client.render.spell; +import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell; +import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; +import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; +import com.minelittlepony.unicopia.util.ColorHelper; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.model.json.ModelTransformationMode; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; -public interface SpellRenderer { - void render(MatrixStack matrices, VertexConsumerProvider vertices, T spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch); +public class SpellRenderer { + public static final SpellRenderer DEFAULT = new SpellRenderer<>(); + + protected final MinecraftClient client = MinecraftClient.getInstance(); + + public boolean shouldRenderEffectPass(int pass) { + return true; + } + + public void render(MatrixStack matrices, VertexConsumerProvider vertices, T spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + if (caster.asEntity() == client.cameraEntity || (caster.asEntity() instanceof MagicProjectileEntity)) { + return; + } + + if (EquinePredicates.IS_CASTER.test(client.player)) { + renderGemstone(matrices, vertices, spell, caster, light, tickDelta, animationProgress); + } + } + + private void renderGemstone(MatrixStack matrices, VertexConsumerProvider vertices, T spell, Caster caster, int light, float tickDelta, float animationProgress) { + matrices.push(); + + transformGemstone(matrices, vertices, spell, caster, animationProgress); + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(animationProgress)); + + client.getItemRenderer().renderItem(spell.getType().withTraits(spell.getTraits()).getDefaultStack(), ModelTransformationMode.FIXED, light, 0, matrices, vertices, caster.asWorld(), 0); + matrices.pop(); + + if (spell instanceof TimedSpell timed) { + renderCountdown(matrices, timed, tickDelta); + } + + matrices.pop(); + } + + protected void renderCountdown(MatrixStack matrices, TimedSpell spell, float tickDelta) { + matrices.multiply(client.getEntityRenderDispatcher().getRotation().invert()); + float radius = 0.6F; + float timeRemaining = spell.getTimer().getPercentTimeRemaining(tickDelta); + + DrawableUtil.drawArc(matrices, radius, radius + 0.3F, 0, DrawableUtil.TAU * timeRemaining, + ColorHelper.lerp(MathHelper.clamp(timeRemaining * 4, 0, 1), 0xFF0000FF, 0xFFFFFFFF), + false + ); + } + + protected void transformGemstone(MatrixStack matrices, VertexConsumerProvider vertices, T spell, Caster caster, float animationProgress) { + float y = -caster.asEntity().getHeight(); + if (caster.asEntity() instanceof CastSpellEntity) { + y = 1F; + } + matrices.translate(0, y + MathHelper.sin(animationProgress / 3F) * 0.2F, 0); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java b/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java index 6282866a..94d5525a 100644 --- a/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java +++ b/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java @@ -13,6 +13,7 @@ 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.TransformCropsRecipe; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.URecipes; import com.minelittlepony.unicopia.item.group.MultiItem; @@ -20,6 +21,7 @@ import com.minelittlepony.unicopia.item.group.MultiItem; import dev.emi.emi.api.EmiPlugin; import dev.emi.emi.api.EmiRegistry; import dev.emi.emi.api.recipe.EmiRecipeCategory; +import dev.emi.emi.api.recipe.EmiWorldInteractionRecipe; import dev.emi.emi.api.render.EmiTexture; import dev.emi.emi.api.stack.Comparison; import dev.emi.emi.api.stack.EmiStack; @@ -33,8 +35,10 @@ 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 EmiStack GROWING_STATION = EmiStack.of(UItems.EARTH_BADGE); 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 EmiRecipeCategory GROWING_CATEGORY = new EmiRecipeCategory(Unicopia.id("growing"), GROWING_STATION, GROWING_STATION); static final Identifier WIDGETS = Unicopia.id("textures/gui/widgets.png"); static final EmiTexture EMPTY_ARROW = new EmiTexture(WIDGETS, 44, 0, 24, 17); @@ -103,5 +107,20 @@ public class Main implements EmiPlugin { }); } }); + + registry.addCategory(GROWING_CATEGORY); + registry.addWorkstation(GROWING_CATEGORY, GROWING_STATION); + registry.getRecipeManager().listAllOfType(URecipes.GROWING).forEach(recipe -> { + registry.addRecipe(new EmiWorldInteractionRecipe(EmiWorldInteractionRecipe.builder() + .id(recipe.id()) + .leftInput(EmiStack.of(recipe.value().getTargetAsItem())) + .rightInput(EmiStack.of(recipe.value().getCatalyst(), TransformCropsRecipe.MINIMUM_INPUT), true) + .output(EmiStack.of(recipe.value().getOutput()))) { + @Override + public EmiRecipeCategory getCategory() { + return GROWING_CATEGORY; + } + }); + }); } } diff --git a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java b/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java index 252c934c..c954ade1 100644 --- a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java +++ b/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java @@ -57,7 +57,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab dirty = false; MsgServerResources msg = new MsgServerResources(); server.getWorlds().forEach(world -> { - Channel.SERVER_RESOURCES_SEND.sendToAllPlayers(msg, world); + Channel.SERVER_RESOURCES.sendToAllPlayers(msg, world); }); } } @@ -84,6 +84,14 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab } } + private static Text readText(JsonElement json) { + return json.isJsonPrimitive() ? Text.translatable(json.getAsString()) : Text.Serialization.fromJsonTree(json); + } + + public enum Flow { + NONE, LEFT, RIGHT + } + public record Chapter ( Identifier id, TabSide side, @@ -106,7 +114,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab .filter(pages -> pages.size() > 0) .stream() .flatMap(pages -> StreamSupport.stream(pages.spliterator(), false)) - .map(Page::new) + .map(Page::of) .toList(); } @@ -125,15 +133,21 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab int level, List elements ) { + private static final Page EMPTY = new Page(Text.empty(), 0, List.of()); + + public static Page of(JsonElement json) { + return json.isJsonObject() && json.getAsJsonObject().keySet().isEmpty() ? EMPTY : new Page(json); + } + @Deprecated - public Page(JsonElement json) { + Page(JsonElement json) { this(json.getAsJsonObject()); } @Deprecated - public Page(JsonObject json) { + Page(JsonObject json) { this( - Text.Serialization.fromJsonTree(json.get("title")), + readText(json.get("title")), JsonHelper.getInt(json, "level", 0), new ArrayList<>() ); @@ -153,10 +167,6 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab } } - public enum Flow { - NONE, LEFT, RIGHT - } - private interface Element { void toBuffer(PacketByteBuf buffer); @@ -219,7 +229,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab return new Multi(count, new Id((byte)4, Identifier.tryParse(json.get("spell").getAsString()))); } - return new Multi(count, new TextBlock(Text.Serialization.fromJsonTree(json.get("text")))); + return new Multi(count, new TextBlock(readText(json.get("text")))); } @Override @@ -236,7 +246,6 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab @Deprecated static Element read(JsonElement json) { if (!json.isJsonPrimitive()) { - JsonObject el = JsonHelper.asObject(json, "element"); if (el.has("texture")) { return new Image( @@ -263,7 +272,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab } } - return new TextBlock(Text.Serialization.fromJsonTree(json)); + return new TextBlock(readText(json)); } private static Bounds boundsFromJson(JsonObject el) { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java index eec0a798..82039a7f 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java @@ -60,7 +60,7 @@ public class Creature extends Living implements WeaklyOwned.Mutabl private boolean discordedChanged = true; private int smittenTicks; - private final Predicate targetPredicate = TargetSelecter.notOwnerOrFriend(() -> getOriginatingCaster().getAffinity(), this).and(e -> { + private final Predicate targetPredicate = TargetSelecter.validTarget(() -> getOriginatingCaster().getAffinity(), this).and(e -> { return Equine.of(e) .filter(eq -> eq instanceof Creature) .filter(eq -> isDiscorded() != ((Creature)eq).hasCommonOwner(this)) diff --git a/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java b/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java index 737f7775..b28faeea 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java @@ -30,6 +30,7 @@ public interface UDamageTypes { RegistryKey SUNLIGHT = register("sunlight"); RegistryKey PETRIFIED = register("petrified"); RegistryKey ROCK = register("rock"); + RegistryKey HORSESHOE = register("horseshoe"); private static RegistryKey register(String name) { var key = RegistryKey.of(RegistryKeys.DAMAGE_TYPE, Unicopia.id(name)); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/MetamorphosisStatusEffect.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/MetamorphosisStatusEffect.java new file mode 100644 index 00000000..56accf6f --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/MetamorphosisStatusEffect.java @@ -0,0 +1,60 @@ +package com.minelittlepony.unicopia.entity.effect; + +import java.util.HashMap; +import java.util.Map; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.Race; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffectCategory; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; + +public class MetamorphosisStatusEffect extends StatusEffect { + public static final int MAX_DURATION = 20 * 60; + + private static final Map REGISTRY = new HashMap<>(); + + public static final StatusEffect EARTH = register(0x886F0F, Race.EARTH); + public static final StatusEffect UNICORN = register(0x88FFFF, Race.UNICORN); + public static final StatusEffect PEGASUS = register(0x00C0ff, Race.PEGASUS); + public static final StatusEffect BAT = register(0x152F13, Race.BAT); + public static final StatusEffect CHANGELING = register(0xFFFF00, Race.CHANGELING); + public static final StatusEffect KIRIN = register(0xFF8800, Race.KIRIN); + public static final StatusEffect HIPPOGRIFF = register(0xE04F77, Race.HIPPOGRIFF); + + @Nullable + public static StatusEffect forRace(Race race) { + return REGISTRY.get(race); + } + + public static StatusEffect register(int color, Race race) { + Identifier id = Race.REGISTRY.getId(race); + StatusEffect effect = new MetamorphosisStatusEffect(color, race); + REGISTRY.put(race, effect); + return Registry.register(Registries.STATUS_EFFECT, + id.withPath(p -> "morph_race_" + p), + effect + ); + } + + public static Race getEffectiveRace(LivingEntity entity, Race fallback) { + return entity.getStatusEffects().stream().filter(effect -> effect.getEffectType() instanceof MetamorphosisStatusEffect).map(effect -> { + return ((MetamorphosisStatusEffect)effect.getEffectType()).getRace(); + }).findFirst().orElse(fallback); + } + + private final Race race; + + private MetamorphosisStatusEffect(int color, Race race) { + super(StatusEffectCategory.NEUTRAL, color); + this.race = race; + } + + public Race getRace() { + return race; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java index 462936ad..210395b9 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java @@ -1,5 +1,8 @@ package com.minelittlepony.unicopia.entity.effect; +import java.util.HashMap; +import java.util.Map; + import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.Race; @@ -28,25 +31,31 @@ public class RaceChangeStatusEffect extends StatusEffect { public static final int STAGE_DURATION = 200; public static final int MAX_DURATION = Stage.VALUES.length * STAGE_DURATION + 1; - public static final StatusEffect CHANGE_RACE_EARTH = register(0x886F0F, Race.EARTH); - public static final StatusEffect CHANGE_RACE_UNICORN = register(0x88FFFF, Race.UNICORN); - public static final StatusEffect CHANGE_RACE_PEGASUS = register(0x00FFFF, Race.PEGASUS); - public static final StatusEffect CHANGE_RACE_BAT = register(0x0FFF00, Race.BAT); - public static final StatusEffect CHANGE_RACE_CHANGELING = register(0xFFFF00, Race.CHANGELING); - public static final StatusEffect CHANGE_RACE_KIRIN = register(0xFF8800, Race.KIRIN); - public static final StatusEffect CHANGE_RACE_HIPPOGRIFF = register(0x00FFFF, Race.HIPPOGRIFF); + private static final Map REGISTRY = new HashMap<>(); + + @Nullable + public static StatusEffect forRace(Race race) { + return REGISTRY.get(race); + } + + public static final StatusEffect EARTH = register(0x886F0F, Race.EARTH); + public static final StatusEffect UNICORN = register(0x88FFFF, Race.UNICORN); + public static final StatusEffect PEGASUS = register(0x00C0ff, Race.PEGASUS); + public static final StatusEffect BAT = register(0x152F13, Race.BAT); + public static final StatusEffect CHANGELING = register(0xFFFF00, Race.CHANGELING); + public static final StatusEffect KIRIN = register(0xFF8800, Race.KIRIN); + public static final StatusEffect HIPPOGRIFF = register(0xE04F77, Race.HIPPOGRIFF); private final Race race; public static StatusEffect register(int color, Race race) { - Identifier id = Race.REGISTRY.getId(race); - return Registry.register(Registries.STATUS_EFFECT, - new Identifier(id.getNamespace(), "change_race_" + id.getPath().toLowerCase()), - new RaceChangeStatusEffect(color, race) - ); + Identifier id = race.getId(); + StatusEffect effect = new RaceChangeStatusEffect(color, race); + REGISTRY.put(race, effect); + return Registry.register(Registries.STATUS_EFFECT, id.withPath(p -> "change_race_" + id.getPath()), effect); } - public RaceChangeStatusEffect(int color, Race race) { + private RaceChangeStatusEffect(int color, Race race) { super(StatusEffectCategory.NEUTRAL, color); this.race = race; } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java index 82f5a0b3..2fb07320 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java @@ -1,29 +1,70 @@ package com.minelittlepony.unicopia.entity.effect; -import java.util.ArrayList; -import java.util.List; +import java.util.Objects; +import org.spongepowered.include.com.google.common.base.Preconditions; + +import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.item.UItems; +import net.minecraft.entity.effect.StatusEffect; import net.minecraft.entity.effect.StatusEffectInstance; +import net.minecraft.item.Item; +import net.minecraft.item.Items; import net.minecraft.potion.Potion; +import net.minecraft.potion.Potions; +import net.minecraft.recipe.BrewingRecipeRegistry; import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; import net.minecraft.registry.Registries; public interface UPotions { - List REGISTRY = new ArrayList<>(); - - Potion TRIBE_SWAP_EARTH_PONY = register("tribe_swap_earth", new Potion("unicopia.tribe_swap_earth", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_EARTH, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_UNICORN = register("tribe_swap_unicorn", new Potion("unicopia.tribe_swap_unicorn", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_UNICORN, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_PEGASUS = register("tribe_swap_pegasus", new Potion("unicopia.tribe_swap_pegasus", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_PEGASUS, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_BAT = register("tribe_swap_bat", new Potion("unicopia.tribe_swap_bat", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_BAT, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_CHANGELING = register("tribe_swap_changeling", new Potion("unicopia.tribe_swap_changeling", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_CHANGELING, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_KIRIN = register("tribe_swap_kirin", new Potion("unicopia.tribe_swap_kirin", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_KIRIN, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_HIPPOGRIFF = register("tribe_swap_hippogriff", new Potion("unicopia.tribe_swap_hippogriff", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_HIPPOGRIFF, RaceChangeStatusEffect.MAX_DURATION))); + MorphingPotion MORPH_EARTH_PONY = new MorphingPotion(Race.EARTH).registerBaseRecipes(Potions.STRENGTH, UItems.CURING_JOKE); + MorphingPotion MORPH_UNICORN = new MorphingPotion(Race.UNICORN).registerBaseRecipes(Potions.REGENERATION, UItems.BOTCHED_GEM); + MorphingPotion MORPH_PEGASUS = new MorphingPotion(Race.PEGASUS).registerBaseRecipes(Potions.SWIFTNESS, UItems.PEGASUS_FEATHER, UItems.HIPPOGRIFF_BADGE, Items.FEATHER); + MorphingPotion MORPH_BAT = new MorphingPotion(Race.BAT).registerBaseRecipes(Potions.NIGHT_VISION, UItems.BUTTERFLY); + MorphingPotion MORPH_CHANGELING = new MorphingPotion(Race.CHANGELING).registerBaseRecipes(Potions.HARMING, UItems.CARAPACE); + MorphingPotion MORPH_KIRIN = new MorphingPotion(Race.KIRIN).registerBaseRecipes(Potions.FIRE_RESISTANCE, Items.MAGMA_CREAM); + MorphingPotion MORPH_HIPPOGRIFF = new MorphingPotion(Race.HIPPOGRIFF).registerBaseRecipes(Potions.WATER_BREATHING, UItems.CLAM_SHELL, UItems.TURRET_SHELL, UItems.SCALLOP_SHELL); static Potion register(String name, Potion potion) { - REGISTRY.add(potion); - return Registry.register(Registries.POTION, Unicopia.id(name), potion); + return register(Unicopia.id(name), potion); + } + + static Potion register(Identifier id, Potion potion) { + return Registry.register(Registries.POTION, id, potion); + } + + static void addRecipe(Potion result, Potion basePotion, Item...items) { + Preconditions.checkArgument(BrewingRecipeRegistry.isBrewable(basePotion), "Base potion is not craftable. " + Registries.POTION.getId(basePotion) + " required for crafting " + Registries.POTION.getId(result)); + for (Item item : items) { + BrewingRecipeRegistry.registerPotionRecipe(basePotion, item, result); + } + } + + record MorphingPotion(Identifier id, Potion shortEffect, Potion longEffect, Potion permanentEffect) { + public MorphingPotion(Race race) { + this(race.getId(), + Objects.requireNonNull(MetamorphosisStatusEffect.forRace(race), "No metamorphosis status effect registered for " + race.getId()), + Objects.requireNonNull(RaceChangeStatusEffect.forRace(race), "No race change status effect registered for " + race.getId()) + ); + } + + public MorphingPotion(Identifier id, StatusEffect morphEffect, StatusEffect permanentEffect) { + this(id, + register(id.withPath(p -> "short_morph_" + p), new Potion(id.getNamespace() + ".short_morph_" + id.getPath(), new StatusEffectInstance(morphEffect, MetamorphosisStatusEffect.MAX_DURATION))), + register(id.withPath(p -> "long_morph_" + p), new Potion(id.getNamespace() + ".long_morph_" + id.getPath(), new StatusEffectInstance(morphEffect, MetamorphosisStatusEffect.MAX_DURATION * 10))), + register(id, new Potion(id.getNamespace() + ".tribe_swap_" + id.getPath(), new StatusEffectInstance(permanentEffect, RaceChangeStatusEffect.MAX_DURATION))) + ); + } + + public MorphingPotion registerBaseRecipes(Potion basePotion, Item...items) { + addRecipe(shortEffect, basePotion, items); + addRecipe(longEffect, shortEffect, Items.REDSTONE); + addRecipe(permanentEffect, longEffect, UItems.CURING_JOKE); + return this; + } } static void bootstrap() { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java index df9c7e37..92b1a909 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java @@ -6,6 +6,7 @@ import com.minelittlepony.unicopia.ability.magic.Levelled; import com.minelittlepony.unicopia.ability.magic.SpellContainer; import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.EntityPhysics; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.MagicImmune; @@ -13,6 +14,8 @@ import com.minelittlepony.unicopia.entity.Physics; import com.minelittlepony.unicopia.network.datasync.EffectSync; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityDimensions; +import net.minecraft.entity.EntityPose; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.data.DataTracker; @@ -37,6 +40,7 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster type, World world) { super(type, world); + ignoreCameraFrustum = true; } @Override @@ -71,6 +75,11 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster spell.getScale(1)).orElse(1F)); + } + @Override public EntityReference getMasterReference() { return owner; @@ -112,6 +121,11 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster ANGRY = DataTracker.registerData(IgnominiousBulbEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private static final TrackedData AGE = DataTracker.registerData(IgnominiousBulbEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final int BABY_AGE = PassiveEntity.BABY_AGE; + private static final int HAPPY_TICKS = 40; + private static final List TENTACLE_OFFSETS = List.of( + new BlockPos(-3, 0, -3), new BlockPos(0, 0, -4), new BlockPos(3, 0, -3), + new BlockPos(-4, 0, 0), new BlockPos(4, 0, 0), + new BlockPos(-3, 0, 3), new BlockPos(0, 0, 4), new BlockPos(3, 0, 4) + ); + + @Nullable + private Map> tentacles; + + private int prevAge; + private int happyTicks; + private int angryTicks; + + public IgnominiousBulbEntity(EntityType type, World world) { + super(type, world); + } + + public IgnominiousBulbEntity(World world) { + super(UEntities.IGNOMINIOUS_BULB, world); + } + + @Override + protected void initDataTracker() { + super.initDataTracker(); + dataTracker.startTracking(ANGRY, false); + dataTracker.startTracking(AGE, 0); + } + + @Override + public boolean isBaby() { + return getAge() < 0; + } + + @Override + public void setBaby(boolean baby) { + setAge(BABY_AGE); + } + + protected int getAge() { + return dataTracker.get(AGE); + } + + protected void setAge(int age) { + dataTracker.set(AGE, age); + } + + public float getScale(float tickDelta) { + return Math.max(0.2F, 1 - (MathHelper.clamp(MathHelper.lerp(tickDelta, prevAge, getAge()), BABY_AGE, 0F) / BABY_AGE)); + } + + public boolean isAngry() { + return dataTracker.get(ANGRY); + } + + public void setAngry(boolean angry) { + if (angry != isAngry()) { + dataTracker.set(ANGRY, angry); + } + } + + public void setAngryFor(int angryTicks) { + this.angryTicks = angryTicks; + } + + @Override + protected ActionResult interactMob(PlayerEntity player, Hand hand) { + + ItemStack stack = player.getStackInHand(hand); + if (isBaby() && stack.isOf(Items.BONE_MEAL)) { + if (!player.isCreative()) { + stack.decrement(1); + } + growUp(10); + if (!getWorld().isClient) { + getWorld().syncWorldEvent(WorldEvents.BONE_MEAL_USED, getBlockPos(), 0); + } + return ActionResult.SUCCESS; + } + + return ActionResult.PASS; + } + + @Override + protected void onPlayerSpawnedChild(PlayerEntity player, MobEntity child) { + Vec3d center = getPos(); + Supplier offset = VecHelper.supplier(() -> (getWorld().random.nextBoolean() ? 1 : -1) * 3); + setPosition(center.add(offset.get().multiply(1, 0, 1))); + child.setPosition(center.add(offset.get().multiply(1, 0, 1))); + } + + @Override + public void setPosition(double x, double y, double z) { + Vec3d change = new Vec3d(x, y, z).subtract(getPos()); + super.setPosition(x, y, z); + getTentacles().values().forEach(tentacle -> { + tentacle.ifPresent(getWorld(), t -> { + t.setPosition(t.getPos().add(change)); + }); + }); + } + + public void growUp(int age) { + int currentAge = getAge(); + if (currentAge < 0) { + setAge((age * 20) + currentAge); + happyTicks = HAPPY_TICKS; + } + } + + @Override + public void tick() { + if (!getWorld().isClient && !isRemoved()) { + var center = new BlockPos.Mutable(); + var tentacles = getTentacles(); + + if (!isBaby() && !firstUpdate) { + TENTACLE_OFFSETS.forEach(offset -> { + tentacles.compute(adjustForTerrain(center, offset), this::updateTentacle); + }); + } + + for (EntityReference tentacle : tentacles.values()) { + if (getWorld().random.nextInt(isAngry() ? 12 : 1200) == 0) { + tentacle.ifPresent(getWorld(), t -> { + t.addActiveTicks(120); + }); + } + } + LivingEntity target = getAttacker(); + + if (angryTicks > 0) { + angryTicks--; + } + + setAngry(!isBaby() && (angryTicks > 0 || target != null)); + + float healthPercentage = getHealth() / getMaxHealth(); + if (isAngry() && target != null && getWorld().random.nextInt(1 + (int)(healthPercentage * 30)) == 0) { + if (target instanceof PlayerEntity player) { + Pony.of(player).getMagicalReserves().getEnergy().add(6); + } + + tentacles.values() + .stream() + .flatMap(tentacle -> tentacle.getOrEmpty(getWorld()).stream()) + .sorted(Comparator.comparing(a -> a.distanceTo(target))) + .limit(2) + .forEach(tentacle -> { + tentacle.setTarget(target); + }); + } + + if (target != null) { + lookAtEntity(target, 10, 10); + } + } + + super.tick(); + } + + @Override + public void tickMovement() { + super.tickMovement(); + prevAge = getAge(); + + if (getWorld().isClient) { + if (happyTicks > 0 && --happyTicks % 4 == 0) { + getWorld().addParticle(ParticleTypes.HAPPY_VILLAGER, getParticleX(1), getRandomBodyY() + 0.5, getParticleZ(1), 0, 0, 0); + } + } else { + if (prevAge < 0) { + setAge(prevAge + 1); + } else if (prevAge > 0) { + setAge(prevAge - 1); + } + } + } + + private Map> getTentacles() { + if (tentacles == null) { + tentacles = getWorld().getEntitiesByClass(TentacleEntity.class, getBoundingBox().expand(5, 0, 5), EntityPredicates.VALID_ENTITY) + .stream() + .collect(Collectors.toMap(TentacleEntity::getBlockPos, tentacle -> { + tentacle.setBulb(this); + return new EntityReference<>(tentacle); + })); + } + return tentacles; + } + + private EntityReference updateTentacle(BlockPos pos, @Nullable EntityReference tentacle) { + if (tentacle == null || tentacle.getOrEmpty(getWorld()).filter(Entity::isAlive).isEmpty()) { + var created = new TentacleEntity(getWorld(), pos); + created.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, getWorld().random.nextFloat() * 360, 0); + created.setBulb(this); + + if (getWorld().isTopSolid(pos.down(), this)) { + getWorld().spawnEntity(created); + } + + return new EntityReference<>(created); + } + return tentacle; + } + + private BlockPos adjustForTerrain(BlockPos.Mutable mutable, BlockPos offset) { + World w = getWorld(); + mutable.set(getBlockPos()); + mutable.move(offset); + while (isSpace(w, mutable.down()) && w.isInBuildLimit(mutable)) { + mutable.move(Direction.DOWN); + } + while (!isPosValid(w, mutable) && w.isInBuildLimit(mutable)) { + mutable.move(Direction.UP); + } + if (w.getBlockState(mutable).isReplaceable()) { + w.breakBlock(mutable, true); + } + return mutable.toImmutable(); + } + + private boolean isPosValid(World w, BlockPos pos) { + return w.isTopSolid(pos.down(), this) && isSpace(w, pos); + } + + private boolean isSpace(World w, BlockPos pos) { + BlockState state = w.getBlockState(pos); + return state.isAir() || state.isReplaceable(); + } + + @Override + public void remove(RemovalReason reason) { + super.remove(reason); + getTentacles().values().forEach(tentacle -> { + tentacle.ifPresent(getWorld(), t -> t.remove(reason)); + }); + } + + @Override + public void move(MovementType movementType, Vec3d movement) { } + + @Override + public void addVelocity(double deltaX, double deltaY, double deltaZ) { } + + @Override + @Nullable + protected SoundEvent getHurtSound(DamageSource source) { + if (isBaby()) { + return SoundEvents.ENTITY_HORSE_BREATHE; + } + return USounds.ENTITY_IGNIMEOUS_BULB_HURT; + } + + @Override + @Nullable + protected SoundEvent getDeathSound() { + return USounds.ENTITY_IGNIMEOUS_BULB_DEATH; + } + + @Override + @Nullable + protected SoundEvent getAmbientSound() { + if (!isBaby() && getWorld().random.nextInt(2) == 0) { + return SoundEvents.ENTITY_CAMEL_AMBIENT; + } + return SoundEvents.ITEM_BONE_MEAL_USE; + } + + @Override + public void writeCustomDataToNbt(NbtCompound nbt) { + super.writeCustomDataToNbt(nbt); + nbt.putBoolean("angry", isAngry()); + nbt.putInt("age", getAge()); + NbtList tentacles = new NbtList(); + getTentacles().forEach((pos, tentacle) -> { + var compound = new NbtCompound(); + compound.put("pos", NbtSerialisable.BLOCK_POS.write(pos)); + compound.put("target", tentacle.toNBT()); + tentacles.add(compound); + }); + nbt.put("tentacles", tentacles); + } + + @Override + public void readCustomDataFromNbt(NbtCompound nbt) { + super.readCustomDataFromNbt(nbt); + setAngry(nbt.getBoolean("angry")); + setAge(nbt.getInt("age")); + if (!getWorld().isClient) { + if (nbt.contains("tentacles", NbtElement.LIST_TYPE)) { + var tentacles = new HashMap>(); + nbt.getList("tentacles", NbtElement.COMPOUND_TYPE).forEach(tag -> { + var compound = (NbtCompound)tag; + tentacles.put(NbtSerialisable.BLOCK_POS.read(compound.getCompound("pos")), new EntityReference<>(compound.getCompound("target"))); + }); + this.tentacles = tentacles; + } + } + } + + @Override + public void onTrackedDataSet(TrackedData data) { + if (AGE.equals(data)) { + calculateDimensions(); + } + super.onTrackedDataSet(data); + } + + @Override + public EntityDimensions getDimensions(EntityPose pose) { + return EntityDimensions.changing(3, 2).scaled(getScale(1)); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java index 22d511cb..72548304 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java @@ -2,9 +2,12 @@ package com.minelittlepony.unicopia.entity.mob; import java.util.Optional; +import org.jetbrains.annotations.Nullable; + import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.UTags; +import com.minelittlepony.unicopia.ability.magic.spell.crafting.AltarRecipeMatch; import com.minelittlepony.unicopia.container.SpellbookScreenHandler; import com.minelittlepony.unicopia.container.SpellbookState; import com.minelittlepony.unicopia.entity.MagicImmune; @@ -19,6 +22,7 @@ import net.fabricmc.fabric.api.util.TriState; import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; @@ -30,6 +34,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.PacketByteBuf; import net.minecraft.particle.ParticleTypes; +import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.screen.*; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; @@ -38,14 +43,19 @@ import net.minecraft.sound.SoundCategory; import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameRules; import net.minecraft.world.World; import net.minecraft.world.explosion.Explosion; +import net.minecraft.world.World.ExplosionSourceType; public class SpellbookEntity extends MobEntity implements MagicImmune { private static final TrackedData LOCKED = DataTracker.registerData(SpellbookEntity.class, TrackedDataHandlerRegistry.BYTE); private static final TrackedData ALTERED = DataTracker.registerData(SpellbookEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private static final byte ALTAR_BEAMS_START = 61; + private static final byte ALTAR_BEAMS_END = 62; private static final int TICKS_TO_SLEEP = 600; @@ -56,6 +66,12 @@ public class SpellbookEntity extends MobEntity implements MagicImmune { private Optional altar = Optional.empty(); + private boolean hasBeams; + private int beamsActive; + + @Nullable + private AltarRecipeMatch activeRecipe; + public SpellbookEntity(EntityType type, World world) { super(type, world); setPersistent(); @@ -105,6 +121,14 @@ public class SpellbookEntity extends MobEntity implements MagicImmune { this.altar = Optional.of(altar); } + public Optional getAltar() { + return altar; + } + + public boolean hasBeams() { + return hasBeams && altar.isPresent(); + } + public boolean isAltered() { return dataTracker.get(ALTERED); } @@ -206,44 +230,83 @@ public class SpellbookEntity extends MobEntity implements MagicImmune { return false; } - altar.pillars().forEach(pillar -> { - Vec3d center = pillar.toCenterPos().add( - random.nextTriangular(0.5, 0.2), - random.nextTriangular(0.5, 0.2), - random.nextTriangular(0.5, 0.2) - ); + tickAltarCrafting(altar); - ((ServerWorld)getWorld()).spawnParticles( - ParticleTypes.SOUL_FIRE_FLAME, - center.x - 0.5, center.y + 0.5, center.z - 0.5, - 0, - 0.5, 0.5, 0.5, 0); - - if (random.nextInt(12) != 0) { - return; - } - - Vec3d vel = center.subtract(this.altar.get().origin().toCenterPos()).normalize(); - - ((ServerWorld)getWorld()).spawnParticles( - ParticleTypes.SOUL_FIRE_FLAME, - center.x - 0.5, center.y + 0.5, center.z - 0.5, - 0, - vel.x, vel.y, vel.z, -0.2); - - if (random.nextInt(2000) == 0) { - if (getWorld().getBlockState(pillar).isOf(Blocks.CRYING_OBSIDIAN)) { - pillar = pillar.down(); - } - getWorld().setBlockState(pillar, Blocks.CRYING_OBSIDIAN.getDefaultState()); - } - }); + Vec3d origin = altar.origin().toCenterPos(); + altar.pillars().forEach(pillar -> tickAltarPillar(origin, pillar)); return true; }); } } + public void setBeamTicks(int ticks) { + getWorld().sendEntityStatus(this, ticks > 0 ? ALTAR_BEAMS_START : ALTAR_BEAMS_END); + beamsActive = ticks; + } + + private void tickAltarCrafting(Altar altar) { + if (activeRecipe == null || activeRecipe.isRemoved()) { + activeRecipe = AltarRecipeMatch.of(getWorld().getEntitiesByClass(ItemEntity.class, Box.of(altar.origin().toCenterPos(), 2, 2, 2), EntityPredicates.VALID_ENTITY)); + + if (activeRecipe != null) { + setBeamTicks(5); + } + } + + if (beamsActive <= 0) { + return; + } + + if (--beamsActive > 0) { + playSound(USounds.Vanilla.ENTITY_GUARDIAN_ATTACK, 1.5F, 0.5F); + return; + } + + //setBeamTicks(0); + + if (activeRecipe == null) { + return; + } + + activeRecipe.craft(); + activeRecipe = null; + getWorld().createExplosion(this, altar.origin().getX(), altar.origin().getY(), altar.origin().getZ(), 0, ExplosionSourceType.NONE); + } + + private void tickAltarPillar(Vec3d origin, BlockPos pillar) { + Vec3d center = pillar.toCenterPos().add( + random.nextTriangular(0.5, 0.2), + random.nextTriangular(0.5, 0.2), + random.nextTriangular(0.5, 0.2) + ); + + ((ServerWorld)getWorld()).spawnParticles( + ParticleTypes.SOUL_FIRE_FLAME, + center.x - 0.5, center.y + 0.5, center.z - 0.5, + 0, + 0.5, 0.5, 0.5, 0); + + if (random.nextInt(12) != 0) { + return; + } + + Vec3d vel = center.subtract(origin).normalize(); + + ((ServerWorld)getWorld()).spawnParticles( + ParticleTypes.SOUL_FIRE_FLAME, + center.x - 0.5, center.y + 0.5, center.z - 0.5, + 0, + vel.x, vel.y, vel.z, -0.2); + + if (random.nextInt(2000) == 0) { + if (getWorld().getBlockState(pillar).isOf(Blocks.CRYING_OBSIDIAN)) { + pillar = pillar.down(); + } + getWorld().setBlockState(pillar, Blocks.CRYING_OBSIDIAN.getDefaultState()); + } + } + private boolean shouldBeSleeping() { return MeteorlogicalUtil.getSkyAngle(getWorld()) > 1 && activeTicks <= 0; } @@ -338,4 +401,19 @@ public class SpellbookEntity extends MobEntity implements MagicImmune { }); Altar.SERIALIZER.writeOptional("altar", compound, altar); } + + @Override + public void handleStatus(byte status) { + switch (status) { + case ALTAR_BEAMS_START: + altar = Altar.locateAltar(getWorld(), getBlockPos()); + hasBeams = altar.isPresent(); + break; + case ALTAR_BEAMS_END: + altar = Optional.empty(); + hasBeams = false; + default: + super.handleStatus(status); + } + } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java new file mode 100644 index 00000000..16d4a459 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java @@ -0,0 +1,341 @@ +package com.minelittlepony.unicopia.entity.mob; + +import java.util.Comparator; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.block.UBlocks; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.minelittlepony.unicopia.util.shape.Sphere; + +import net.minecraft.command.argument.EntityAnchorArgumentType.EntityAnchor; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.MovementType; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.damage.DamageTypes; +import net.minecraft.entity.data.DataTracker; +import net.minecraft.entity.data.TrackedData; +import net.minecraft.entity.data.TrackedDataHandlerRegistry; +import net.minecraft.entity.decoration.AbstractDecorationEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.registry.tag.ItemTags; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.RaycastContext; +import net.minecraft.world.World; + +public class TentacleEntity extends AbstractDecorationEntity { + static final byte ATTACK_STATUS = 54; + static final int MAX_GROWTH = 25; + private static final TrackedData GROWTH = DataTracker.registerData(TentacleEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final TrackedData MOTION_OFFSET = DataTracker.registerData(TentacleEntity.class, TrackedDataHandlerRegistry.INTEGER); + + @Nullable + private Box visibilityBox; + + private int prevGrowth; + private int attackingTicks; + private float prevAttackingTicks; + + private int ticksActive; + private int prevMotionOffset; + + @Nullable + private LivingEntity target; + private final Comparator targetSorting = Comparator.comparing(this::distanceTo); + + @Nullable + private IgnominiousBulbEntity bulb; + + public TentacleEntity(EntityType type, World world) { + super(type, world); + } + + public TentacleEntity(World world, BlockPos pos) { + super(UEntities.TENTACLE, world, pos); + } + + @Override + protected void initDataTracker() { + super.initDataTracker(); + dataTracker.startTracking(GROWTH, 0); + dataTracker.startTracking(MOTION_OFFSET, 0); + } + + public void setBulb(IgnominiousBulbEntity bulb) { + this.bulb = bulb; + } + + public void attack(BlockPos pos) { + var offset = pos.toCenterPos().subtract(getBlockPos().toCenterPos()); + + double dX = offset.x; + double dY = offset.y; + double dZ = offset.z; + double radius = Math.sqrt(dX * dX + dZ * dZ); + + setPitch(MathHelper.wrapDegrees((float)(-(MathHelper.atan2(dY, radius) * MathHelper.DEGREES_PER_RADIAN)))); + setYaw(MathHelper.wrapDegrees((float)(MathHelper.atan2(dZ, dX) * MathHelper.DEGREES_PER_RADIAN) - 90)); + getWorld().sendEntityStatus(this, ATTACK_STATUS); + attackingTicks = 30; + if (bulb != null) { + bulb.setAngryFor(10); + bulb.lookAt(EntityAnchor.FEET, pos.toCenterPos()); + } + } + + public float getAttackProgress(float tickDelta) { + return (30F - MathHelper.lerp(tickDelta, prevAttackingTicks, attackingTicks)) / 30F; + } + + public float getGrowth(float tickDelta) { + return MathHelper.lerp(tickDelta, prevGrowth, getGrowth()) / (float)MAX_GROWTH; + } + + public int getGrowth() { + return dataTracker.get(GROWTH); + } + + public void setGrowth(int growth) { + dataTracker.set(GROWTH, Math.max(0, growth)); + } + + public float getAnimationTimer(float tickDelta) { + return (age + tickDelta + (getUuid().getMostSignificantBits() % 100)) * 0.00043F + + MathHelper.lerp(tickDelta, prevMotionOffset, getMotionOffset()) * 0.002F; + } + + public int getMotionOffset() { + return dataTracker.get(MOTION_OFFSET); + } + + public void setMotionOffset(int motionOffset) { + dataTracker.set(MOTION_OFFSET, motionOffset); + } + + public boolean isAttacking() { + return attackingTicks > 0; + } + + @Override + public int getWidthPixels() { + return 9; + } + + @Override + public int getHeightPixels() { + return 9; + } + + @Override + public boolean damage(DamageSource source, float amount) { + if (source.getAttacker() instanceof PlayerEntity player) { + if (player.getStackInHand(Hand.MAIN_HAND).isIn(ItemTags.AXES)) { + kill(); + ParticleUtils.spawnParticles(ParticleTypes.EFFECT, this, 10); + } + if (getWorld().random.nextInt(5) == 0 && canTarget(player)) { + setTarget(player); + } + } + addActiveTicks(20 + getWorld().random.nextInt(30)); + playSound(USounds.ENTITY_TENTACLE_ROAR, 5, 1); + return true; + } + + public void addActiveTicks(int ticks) { + ticksActive = Math.min(200, ticksActive + ticks); + } + + @Override + public void tick() { + prevMotionOffset = getMotionOffset(); + int growth = getGrowth(); + prevGrowth = growth; + super.tick(); + prevAttackingTicks = attackingTicks; + if (isAttacking()) { + if (--attackingTicks == 12) { + if (target != null) { + target.damage(getDamageSources().create(DamageTypes.MOB_ATTACK, this), 15); + Vec3d diff = target.getPos().subtract(getPos()); + target.takeKnockback(1, diff.x, diff.z); + + ParticleUtils.spawnParticles(ParticleTypes.CLOUD, target, 10); + + for (Entity bystander : getWorld().getOtherEntities(target, target.getBoundingBox().expand(3))) { + if (bystander instanceof LivingEntity l) { + diff = l.getPos().subtract(getPos()); + l.takeKnockback(1, diff.x, diff.z); + ParticleUtils.spawnParticles(ParticleTypes.CLOUD, target, 10); + } + } + + target = null; + } + } + } + + ParticleUtils.spawnParticles(ParticleTypes.ASH, this, 4); + var sphere = new Sphere(false, 10).translate(getPos()); + ParticleUtils.spawnParticles(getWorld(), sphere, ParticleTypes.ASH, 4); + + if (!getWorld().isClient) { + if (growth >= MAX_GROWTH / 2) { + if (age % 50 == 0) { + updateTarget(); + } + + if (target != null && !isAttacking()) { + attack(target.getBlockPos()); + } + } + + if (growth < MAX_GROWTH) { + setGrowth(growth + 1); + + if (growth == 0) { + playSound(USounds.ENTITY_TENTACLE_DIG, 1, 1); + } + } + + if (getWorld().random.nextInt(110) == 0) { + playSound(USounds.ENTITY_TENTACLE_AMBIENT, 1, 0.3F); + } + + if (ticksActive > 0) { + ticksActive--; + setMotionOffset(getMotionOffset() + ticksActive); + } + } else { + if (growth < MAX_GROWTH && age % 15 == getWorld().random.nextInt(14)) { + getWorld().addBlockBreakParticles(getBlockPos().down(), getWorld().getBlockState(getBlockPos().down())); + } + } + } + + public void setTarget(LivingEntity target) { + this.target = target; + playSound(USounds.ENTITY_TENTACLE_ROAR, 5, 1); + + if (target instanceof PlayerEntity player) { + Pony.of(player).getMagicalReserves().getEnergy().add(6); + } + } + + public LivingEntity getTarget() { + return target; + } + + private void updateTarget() { + if (!canTarget(target)) { + target = null; + } + + if (target == null && !isAttacking()) { + getWorld().getEntitiesByClass(HostileEntity.class, getBoundingBox().expand(10, 3, 10), this::canTarget) + .stream() + .sorted(targetSorting) + .findFirst() + .ifPresent(this::setTarget); + } + } + + protected boolean canTarget(LivingEntity target) { + return target != null + && !target.isRemoved() + && !target.isSneaky() + && !(target instanceof PlayerEntity player && (player.isCreative() || player.isSpectator())) + && canSee(target); + } + + private boolean canSee(Entity entity) { + return entity.getWorld() == getWorld() + && distanceTo(entity) <= 128 + && getWorld().raycast(new RaycastContext(getPos(), entity.getPos(), RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, this)).getType() == HitResult.Type.MISS; + } + + @Override + public void handleStatus(byte status) { + switch (status) { + case ATTACK_STATUS: + attackingTicks = 30; + break; + default: + super.handleStatus(status); + } + } + + @Override + public void onBreak(Entity breaker) { + + } + + @Override + public void onPlace() { + + } + + + @Override + public void remove(RemovalReason reason) { + super.remove(reason); + if (getWorld().isAir(getBlockPos()) && getWorld().getBlockState(getBlockPos().down()).isIn(BlockTags.DIRT)) { + getWorld().setBlockState(getBlockPos(), UBlocks.CURING_JOKE.getDefaultState()); + } + } + + + @Override + public boolean canStayAttached() { + return getWorld().isTopSolid(getBlockPos().down(), this); + } + + @Override + public Box getVisibilityBoundingBox() { + if (visibilityBox == null) { + visibilityBox = getBoundingBox().expand(10, 0, 10).stretch(0, 10, 0); + } + return visibilityBox; + } + + @Override + protected void updateAttachmentPosition() { + visibilityBox = null; + Vec3d pos = attachmentPos.toCenterPos(); + setPos(pos.x, pos.y, pos.z); + setBoundingBox(Box.of(pos, 1, 1, 1).stretch(0, 2, 0)); + } + + @Override + public void move(MovementType movementType, Vec3d movement) { } + + @Override + public void addVelocity(double deltaX, double deltaY, double deltaZ) { } + + @Override + public void writeCustomDataToNbt(NbtCompound nbt) { + super.writeCustomDataToNbt(nbt); + nbt.putInt("growth", getGrowth()); + nbt.putInt("motion_offset", getMotionOffset()); + } + + @Override + public void readCustomDataFromNbt(NbtCompound nbt) { + super.readCustomDataFromNbt(nbt); + setGrowth(nbt.getInt("growth")); + setMotionOffset(nbt.getInt("motion_offset")); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java index 5b14063b..9f85407a 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java @@ -42,7 +42,7 @@ public interface UEntities { .dimensions(EntityDimensions.fixed(1, 1))); EntityType CAST_SPELL = register("cast_spell", FabricEntityTypeBuilder.create(SpawnGroup.MISC, CastSpellEntity::new) .trackRangeBlocks(200) - .dimensions(EntityDimensions.fixed(1, 0.4F))); + .dimensions(EntityDimensions.changing(4, 4))); EntityType TWITTERMITE = register("twittermite", FabricEntityTypeBuilder.create(SpawnGroup.MISC, FairyEntity::new) .trackRangeBlocks(200) .dimensions(EntityDimensions.fixed(0.1F, 0.1F))); @@ -68,6 +68,12 @@ public interface UEntities { EntityType LOOT_BUG = register("loot_bug", FabricEntityTypeBuilder.create(SpawnGroup.MONSTER, LootBugEntity::new) .trackRangeChunks(8) .dimensions(EntityDimensions.fixed(0.8F, 0.6F))); + EntityType TENTACLE = register("ignominious_vine", FabricEntityTypeBuilder.create(SpawnGroup.MISC, TentacleEntity::new) + .trackRangeChunks(8) + .dimensions(EntityDimensions.fixed(0.8F, 0.8F))); + EntityType IGNOMINIOUS_BULB = register("ignominious_bulb", FabricEntityTypeBuilder.create(SpawnGroup.MISC, IgnominiousBulbEntity::new) + .trackRangeChunks(8) + .dimensions(EntityDimensions.fixed(3, 2))); static EntityType register(String name, FabricEntityTypeBuilder builder) { EntityType type = builder.build(); @@ -82,6 +88,7 @@ public interface UEntities { FabricDefaultAttributeRegistry.register(SOMBRA, SombraEntity.createMobAttributes()); FabricDefaultAttributeRegistry.register(FRIENDLY_CREEPER, FriendlyCreeperEntity.createCreeperAttributes()); FabricDefaultAttributeRegistry.register(LOOT_BUG, LootBugEntity.createSilverfishAttributes()); + FabricDefaultAttributeRegistry.register(IGNOMINIOUS_BULB, IgnominiousBulbEntity.createMobAttributes()); if (!Unicopia.getConfig().disableButterflySpawning.get()) { final Predicate butterflySpawnable = BiomeSelectors.foundInOverworld() diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java index cedc1064..ff73a2f8 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java @@ -4,7 +4,6 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; -import com.minelittlepony.unicopia.entity.effect.UPotions; import com.minelittlepony.unicopia.item.EnchantableItem; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.util.RegistryUtils; @@ -14,7 +13,6 @@ import net.minecraft.entity.Entity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.potion.PotionUtil; import net.minecraft.registry.tag.ItemTags; import net.minecraft.registry.tag.TagKey; import net.minecraft.util.Util; @@ -22,7 +20,6 @@ import net.minecraft.util.math.random.Random; import net.minecraft.village.TradeOffer; import net.minecraft.village.TradeOffers; import net.minecraft.village.VillagerProfession; -import net.minecraft.village.TradeOffers.Factory; public interface UTradeOffers { static void bootstrap() { @@ -59,10 +56,6 @@ public interface UTradeOffers { factories.add(buy(UItems.CRYSTAL_HEART, 1, UItems.MUSIC_DISC_CRUSADE, 1, 10, 6, 0.08F)); factories.add(buy(UItems.PEGASUS_AMULET, 1, UItems.ALICORN_AMULET, 1, 2, 6, 0.05F)); factories.add(buyForEmeralds(UItems.FRIENDSHIP_BRACELET, 2, 1, 10, 7, 0.17F)); - factories.add(new SellPotionHoldingItemFactory( - new Item[] { Items.ARROW, Items.GLASS_BOTTLE, Items.GLASS_BOTTLE }, - new Item[] { Items.TIPPED_ARROW, Items.POTION, Items.SPLASH_POTION }, - 5, 5, 2, 12, 30)); }); } @@ -105,17 +98,4 @@ public interface UTradeOffers { return new TradeOffer(offer.getOriginalFirstBuyItem(), offer.getSecondBuyItem(), UItems.FILLED_JAR.withContents(offer.getSellItem()), offer.getUses(), offer.getMaxUses(), offer.getMerchantExperience(), offer.getPriceMultiplier(), offer.getDemandBonus()); } } - - record SellPotionHoldingItemFactory (Item[] secondBuy, Item[] tippedArrow, int secondCount, int sellCount, int price, int maxUses, int experience) implements Factory { - @Override - public TradeOffer create(Entity entity, Random random) { - int index = random.nextInt(tippedArrow.length); - return new TradeOffer( - new ItemStack(Items.EMERALD, price), - new ItemStack(secondBuy[index], secondCount), - PotionUtil.setPotion(new ItemStack(tippedArrow[index], sellCount), UPotions.REGISTRY.get(random.nextInt(UPotions.REGISTRY.size()))), - maxUses, experience, 0.05f - ); - } - } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java index 11b71c2e..26c6d4c7 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java @@ -5,6 +5,7 @@ import java.util.Optional; import com.minelittlepony.common.util.animation.MotionCompositor; import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell; +import com.minelittlepony.unicopia.client.render.spell.DarkVortexSpellRenderer; import net.minecraft.util.math.Vec3d; @@ -62,6 +63,7 @@ public class PlayerCamera extends MotionCompositor { public double calculateFieldOfView(double fov) { fov += player.getMagicalReserves().getExertion().get() / 5F; fov += getEnergyAddition(); + fov += DarkVortexSpellRenderer.getCameraDistortion() * 2.5F; return fov; } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java index 20e91f27..aeaf4d73 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.entity.player; +import java.util.Objects; + import com.google.common.collect.Streams; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; @@ -53,7 +55,9 @@ public class PlayerCharmTracker implements NbtSerialisable { public CustomisedSpellType equipSpell(Hand hand, CustomisedSpellType spell) { CustomisedSpellType previous = handSpells[hand.ordinal()]; handSpells[hand.ordinal()] = spell; - pony.asEntity().playSound(USounds.GUI_SPELL_EQUIP.value(), 0.25F, 1.75F); + if (!Objects.equals(previous, spell)) { + pony.asEntity().playSound(USounds.GUI_SPELL_EQUIP.value(), 0.25F, 1.75F); + } pony.setDirty(); return previous; } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java index bb9b68e9..e1a6cd75 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -40,6 +40,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.damage.DamageSource; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; +import net.minecraft.particle.BlockStateParticleEffect; import net.minecraft.particle.ParticleTypes; import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.registry.RegistryKeys; @@ -564,10 +565,14 @@ public class PlayerPhysics extends EntityPhysics implements Tickab entity.calculateDimensions(); if (entity.isOnGround() || !force) { - Supplier pos = VecHelper.sphere(pony.asWorld().getRandom(), 0.5D); - Supplier vel = VecHelper.sphere(pony.asWorld().getRandom(), 0.15D); - pony.spawnParticles(ParticleTypes.CAMPFIRE_COSY_SMOKE, pos, vel, 5); - pony.spawnParticles(ParticleTypes.CLOUD, pos, vel, 5); + BlockState steppingState = pony.asEntity().getSteppingBlockState(); + if (steppingState.isIn(UTags.KICKS_UP_DUST)) { + pony.addParticle(new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), pony.getOrigin().down().toCenterPos(), Vec3d.ZERO); + } else { + Supplier pos = VecHelper.sphere(pony.asWorld().getRandom(), 0.5D); + Supplier vel = VecHelper.sphere(pony.asWorld().getRandom(), 0.015D); + pony.spawnParticles(ParticleTypes.CLOUD, pos, vel, 5); + } } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 6a12cabe..fab87347 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -21,6 +21,7 @@ import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck; +import com.minelittlepony.unicopia.entity.effect.MetamorphosisStatusEffect; import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect; import com.minelittlepony.unicopia.entity.effect.UEffects; import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; @@ -239,8 +240,7 @@ public class Pony extends Living implements Copyable, Update gravity.updateFlightState(); entity.sendAbilitiesUpdate(); - - UCriteria.PLAYER_CHANGE_RACE.trigger(entity); + recalculateCompositeRace(); } public void setSuppressedRace(Race race) { @@ -363,6 +363,11 @@ public class Pony extends Living implements Copyable, Update return asEntity(); } + @Override + public Optional getMasterId() { + return Optional.of(asEntity().getUuid()); + } + public void onSpawn() { if (entity.getWorld() instanceof ServerWorld sw && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE) { boolean mustAvoidSun = getObservedSpecies() == Race.BAT && MeteorlogicalUtil.isPositionExposedToSun(sw, getOrigin()); @@ -377,20 +382,7 @@ public class Pony extends Living implements Copyable, Update @Override public boolean beforeUpdate() { if (compositeRace.includes(Race.UNSET) || entity.age % 2 == 0) { - Race intrinsicRace = getSpecies(); - Race suppressedRace = getSuppressedRace(); - compositeRace = getSpellSlot() - .get(SpellPredicate.IS_MIMIC, true) - .map(AbstractDisguiseSpell::getDisguise) - .map(EntityAppearance::getAppearance) - .flatMap(Pony::of) - .map(Pony::getSpecies) - .orElse(intrinsicRace).composite( - AmuletSelectors.UNICORN_AMULET.test(entity) ? Race.UNICORN - : AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN - : null, - AmuletSelectors.PEARL_NECKLACE.test(entity) ? suppressedRace.or(Race.SEAPONY) : null - ); + recalculateCompositeRace(); } if (ticksInvulnerable > 0) { @@ -482,6 +474,24 @@ public class Pony extends Living implements Copyable, Update return super.beforeUpdate(); } + private void recalculateCompositeRace() { + Race intrinsicRace = getSpecies(); + Race suppressedRace = getSuppressedRace(); + compositeRace = MetamorphosisStatusEffect.getEffectiveRace(entity, getSpellSlot() + .get(SpellPredicate.IS_MIMIC, true) + .map(AbstractDisguiseSpell::getDisguise) + .map(EntityAppearance::getAppearance) + .flatMap(Pony::of) + .map(Pony::getSpecies) + .orElse(intrinsicRace)).composite( + AmuletSelectors.UNICORN_AMULET.test(entity) ? Race.UNICORN + : AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN + : null, + AmuletSelectors.PEARL_NECKLACE.test(entity) ? suppressedRace.or(Race.SEAPONY) : null + ); + UCriteria.PLAYER_CHANGE_RACE.trigger(entity); + } + @Override public Optional chooseClimbingPos() { if (getObservedSpecies() == Race.CHANGELING && getSpellSlot().get(SpellPredicate.IS_DISGUISE, false).isEmpty()) { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java index f7ee0cde..926581a9 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java @@ -1,5 +1,8 @@ package com.minelittlepony.unicopia.entity.player.dummy; +import java.util.Optional; +import java.util.UUID; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,6 +16,7 @@ import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.PlayerListEntry; import net.minecraft.client.render.entity.PlayerModelPart; import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; @@ -79,4 +83,9 @@ public class DummyClientPlayerEntity extends AbstractClientPlayerEntity implemen public void setMaster(PlayerEntity owner) { this.owner = owner; } + + @Override + public Optional getMasterId() { + return Optional.ofNullable(owner).map(Entity::getUuid); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java index 798dc6e1..c5ce8f09 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java @@ -1,5 +1,8 @@ package com.minelittlepony.unicopia.entity.player.dummy; +import java.util.Optional; +import java.util.UUID; + import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.InteractionManager; @@ -7,6 +10,7 @@ import com.minelittlepony.unicopia.Owned; import com.mojang.authlib.GameProfile; import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; @@ -42,6 +46,11 @@ public class DummyPlayerEntity extends PlayerEntity implements Owned getMasterId() { + return Optional.ofNullable(owner).map(Entity::getUuid); + } + @Override public boolean shouldRenderName() { return !InteractionManager.instance().isClientPlayer(getMaster()); diff --git a/src/main/java/com/minelittlepony/unicopia/item/CuringJokeItem.java b/src/main/java/com/minelittlepony/unicopia/item/CuringJokeItem.java new file mode 100644 index 00000000..cbd12b9a --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/CuringJokeItem.java @@ -0,0 +1,119 @@ +package com.minelittlepony.unicopia.item; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.particle.MagicParticleEffect; +import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.minelittlepony.unicopia.util.InventoryUtil; + +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.InventoryOwner; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public class CuringJokeItem extends BlockItem { + static final List> EFFECTS = List.of( + CuringJokeItem::restoreAir, + CuringJokeItem::restoreHunger, + CuringJokeItem::restoreHealth, + CuringJokeItem::removeEffect, + CuringJokeItem::uncurseItem, + CuringJokeItem::repairItem + ); + + public CuringJokeItem(Block block, Settings settings) { + super(block, settings); + } + + @Override + public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) { + var items = new ArrayList<>(EFFECTS); + while (!items.isEmpty()) { + if (items.remove(world.random.nextInt(items.size())).test(user)) { + ParticleUtils.spawnParticles(new MagicParticleEffect(0x3388EE), user, 25); + world.playSound(null, user.getBlockPos(), USounds.ITEM_CURING_JOKE_CURE, user.getSoundCategory(), 1, 1); + break; + } + } + + return super.finishUsing(stack, world, user); + } + + static boolean restoreAir(LivingEntity user) { + if (user.getAir() < user.getMaxAir()) { + user.setAir(user.getMaxAir()); + return true; + } + return false; + } + + static boolean restoreHunger(LivingEntity user) { + if (user instanceof PlayerEntity player && player.getHungerManager().getFoodLevel() < 20) { + player.getHungerManager().add(20, 0); + return true; + } + return false; + } + + static boolean restoreHealth(LivingEntity user) { + if (user.getHealth() < user.getMaxHealth()) { + user.setHealth(user.getMaxHealth()); + return true; + } + return false; + } + + static boolean removeEffect(LivingEntity user) { + return user.getStatusEffects().stream().filter(effect -> { + return !effect.getEffectType().isBeneficial(); + }).findAny().filter(effect -> { + user.removeStatusEffect(effect.getEffectType()); + return true; + }).isPresent(); + } + + static boolean uncurseItem(LivingEntity user) { + return getInventory(user) + .filter(s -> EnchantmentHelper.get(s).keySet().stream().anyMatch(Enchantment::isCursed)) + .findAny() + .filter(s -> { + var enchantments = EnchantmentHelper.get(s); + return enchantments.keySet().stream().filter(Enchantment::isCursed).findAny().filter(e -> { + enchantments.remove(e); + EnchantmentHelper.set(enchantments, s); + return true; + }).isPresent(); + }).isPresent(); + } + + static boolean repairItem(LivingEntity user) { + return getInventory(user) + .filter(s -> s.getDamage() < s.getMaxDamage()) + .findAny().filter(s -> { + s.setDamage(0); + return true; + }).isPresent(); + } + + static Stream getInventory(LivingEntity entity) { + if (entity instanceof PlayerEntity player) { + return InventoryUtil.stream(player.getInventory()); + } + + if (entity instanceof InventoryOwner owner) { + return InventoryUtil.stream(owner.getInventory()); + } + + return StreamSupport.stream(entity.getItemsEquipped().spliterator(), false); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java b/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java index 326ddd43..4062ae4a 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java @@ -7,6 +7,7 @@ import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquinePredicates; +import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate; @@ -78,7 +79,7 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem, @Override public EquipmentSlot getSlotType(ItemStack stack) { - return isSigned(stack) ? EquipmentSlot.CHEST : super.getSlotType(); + return isSigned(stack) ? EquipmentSlot.CHEST : super.getSlotType(stack); } private boolean checkSignature(ItemStack stack, PlayerEntity player) { @@ -113,15 +114,10 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem, && ((FriendshipBraceletItem)stack.getItem()).checkSignature(stack, player); } - public static boolean isComrade(Caster caster, Entity entity) { - if (entity instanceof LivingEntity) { - return caster.getMasterId() - .filter(id -> getWornBangles((LivingEntity)entity) - .anyMatch(stack -> isSignedBy(stack, id)) - ) - .isPresent(); - } - return false; + public static boolean isComrade(Owned caster, Entity entity) { + return entity instanceof LivingEntity l && caster.getMasterId() + .filter(id -> getWornBangles(l).anyMatch(stack -> isSignedBy(stack, id))) + .isPresent(); } public static Stream getPartyMembers(Caster caster, double radius) { diff --git a/src/main/java/com/minelittlepony/unicopia/item/HorseShoeItem.java b/src/main/java/com/minelittlepony/unicopia/item/HorseShoeItem.java index a7899e8d..a9ed6129 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/HorseShoeItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/HorseShoeItem.java @@ -9,6 +9,7 @@ import com.google.common.collect.Multimap; import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.projectile.PhysicsBodyProjectileEntity; @@ -75,6 +76,7 @@ public class HorseShoeItem extends HeavyProjectileItem { @Override protected PhysicsBodyProjectileEntity createProjectile(ItemStack stack, World world, @Nullable PlayerEntity player) { PhysicsBodyProjectileEntity projectile = super.createProjectile(stack, world, player); + projectile.setDamageType(UDamageTypes.HORSESHOE); float degradation = (stack.getDamage() / (float)stack.getMaxDamage()); diff --git a/src/main/java/com/minelittlepony/unicopia/item/TraitItem.java b/src/main/java/com/minelittlepony/unicopia/item/TraitItem.java new file mode 100644 index 00000000..c3b95049 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/TraitItem.java @@ -0,0 +1,21 @@ +package com.minelittlepony.unicopia.item; + +import com.minelittlepony.unicopia.ability.magic.spell.trait.ItemWithTraits; +import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; +import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; + +import net.minecraft.item.Item; + +public class TraitItem extends Item implements ItemWithTraits { + private final SpellTraits traits; + + public TraitItem(Trait trait, Settings settings) { + super(settings); + this.traits = new SpellTraits.Builder().with(trait, 1).build(); + } + + @Override + public SpellTraits getDefaultTraits() { + return traits; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java b/src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java new file mode 100644 index 00000000..94a96379 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java @@ -0,0 +1,161 @@ +package com.minelittlepony.unicopia.item; + +import java.util.HashSet; +import java.util.Set; + +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.inventory.SingleStackInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeSerializer; +import net.minecraft.recipe.RecipeType; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.registry.Registries; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.World; + +public class TransformCropsRecipe implements Recipe { + public static final int RADIUS = 3; + public static final int SIDE_LENGTH = (2 * RADIUS) + 1; + public static final int AREA = (SIDE_LENGTH * SIDE_LENGTH) - 1; + public static final int MINIMUM_INPUT = 9; + + private final Block target; + private final BlockState catalyst; + private final BlockState output; + + public TransformCropsRecipe(Block target, BlockState catalyst, BlockState output) { + this.output = output; + this.target = target; + this.catalyst = catalyst; + } + + public ItemStack getTargetAsItem() { + return target.asItem().getDefaultStack(); + } + + public ItemStack getCatalyst() { + return catalyst.getBlock().asItem().getDefaultStack(); + } + + public ItemStack getOutput() { + return output.getBlock().asItem().getDefaultStack(); + } + + @Override + public RecipeSerializer getSerializer() { + return URecipes.TRANSFORM_CROP_SERIALIZER; + } + + @Override + public RecipeType getType() { + return URecipes.GROWING; + } + + @Override + public boolean matches(PlacementArea inventory, World world) { + return world.getBlockState(inventory.position()).isOf(target); + } + + @Override + public ItemStack craft(PlacementArea inventory, DynamicRegistryManager manager) { + return getResult(manager); + } + + @Override + public ItemStack getResult(DynamicRegistryManager manager) { + return output.getBlock().asItem().getDefaultStack(); + } + + public Result checkPattern(World world, BlockPos pos) { + BlockPos center = pos.down(); + Set matches = new HashSet<>(); + for (BlockPos cell : BlockPos.iterateInSquare(center, RADIUS, Direction.EAST, Direction.NORTH)) { + if (cell.equals(center)) { + continue; + } + if (!world.getBlockState(cell).equals(catalyst)) { + break; + } + matches.add(cell.toImmutable()); + } + return new Result(this, matches); + } + + public BlockState getResult(World world, BlockPos pos) { + return StateUtil.copyState(world.getBlockState(pos), output); + } + + @Override + public boolean fits(int width, int height) { + return width >= SIDE_LENGTH && height >= SIDE_LENGTH; + } + + public static class Serializer implements RecipeSerializer { + private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Registries.BLOCK.getCodec().fieldOf("target").forGetter(recipe -> recipe.target), + BlockState.CODEC.fieldOf("consume").forGetter(recipe -> recipe.catalyst), + BlockState.CODEC.fieldOf("output").forGetter(recipe -> recipe.output) + ).apply(instance, TransformCropsRecipe::new)); + + @Override + public Codec codec() { + return CODEC; + } + + @Override + public TransformCropsRecipe read(PacketByteBuf buffer) { + return new TransformCropsRecipe( + buffer.readRegistryValue(Registries.BLOCK), + Block.getStateFromRawId(buffer.readInt()), + Block.getStateFromRawId(buffer.readInt()) + ); + } + + @Override + public void write(PacketByteBuf buffer, TransformCropsRecipe recipe) { + buffer.writeRegistryValue(Registries.BLOCK, recipe.target); + buffer.writeInt(Block.getRawIdFromState(recipe.catalyst)); + buffer.writeInt(Block.getRawIdFromState(recipe.output)); + } + } + + public static record PlacementArea (Pony pony, BlockPos position) implements SingleStackInventory { + @Override + public void markDirty() { } + + @Override + public ItemStack getStack() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack decreaseStack(int var1) { + return ItemStack.EMPTY; + } + + @Override + public void setStack(ItemStack var1) { } + + @Override + public BlockEntity asBlockEntity() { + return null; + } + } + + public record Result (TransformCropsRecipe recipe, Set matchedLocations) { + public boolean shoudTransform(Random random) { + return random.nextInt(AREA) < matchedLocations().size(); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java b/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java index 24dcf544..a7ff322a 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java @@ -53,6 +53,8 @@ public interface UFoodComponents { FoodComponent CANDY = builder(7, 0.9F).alwaysEdible().build(); FoodComponent SALT_CUBE = builder(0, 2.9F).alwaysEdible().build(); + FoodComponent POISON_JOKE = builder(0, 0F).alwaysEdible().snack().build(); + @Deprecated FoodComponent SHELL = builder(3, 5).build(); @Deprecated diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index e9863be6..cf71cf2a 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -48,6 +48,7 @@ public interface UItems { FriendshipBraceletItem FRIENDSHIP_BRACELET = register("friendship_bracelet", new FriendshipBraceletItem(new FabricItemSettings().rarity(Rarity.UNCOMMON)), ItemGroups.TOOLS); + Item PLUNDER_VINE = register("plunder_vine", new BlockItem(UBlocks.PLUNDER_VINE_BUD, new Item.Settings())); Item EMPTY_JAR = register("empty_jar", new JarItem(new Item.Settings().maxCount(16).fireproof(), false, false, false), ItemGroups.FUNCTIONAL); FilledJarItem FILLED_JAR = register("filled_jar", new FilledJarItem(new Item.Settings().maxCount(1).recipeRemainder(EMPTY_JAR))); Item RAIN_CLOUD_JAR = register("rain_cloud_jar", new JarItem(new Item.Settings().maxCount(1).fireproof().recipeRemainder(EMPTY_JAR), true, false, false), ItemGroups.FUNCTIONAL); @@ -88,6 +89,7 @@ public interface UItems { Item ACORN = register("acorn", new Item(new Item.Settings().food(UFoodComponents.ACORN).maxCount(16)), ItemGroups.FOOD_AND_DRINK); Item MANGO = register("mango", new Item(new Item.Settings().food(UFoodComponents.MANGO)), ItemGroups.FOOD_AND_DRINK); Item BANANA = register("banana", new Item(new Item.Settings().food(UFoodComponents.BANANA)), ItemGroups.FOOD_AND_DRINK); + Item CURING_JOKE = register("curing_joke", new CuringJokeItem(UBlocks.CURING_JOKE, new Item.Settings().food(UFoodComponents.POISON_JOKE)), ItemGroups.NATURAL); Item PINEAPPLE = register("pineapple", new PineappleItem(new Item.Settings().food(UFoodComponents.BANANA).maxDamage(3)), ItemGroups.FOOD_AND_DRINK); Item PINEAPPLE_CROWN = register("pineapple_crown", new AliasedBlockItem(UBlocks.PINEAPPLE, new Item.Settings()), ItemGroups.NATURAL); @@ -106,6 +108,7 @@ public interface UItems { Item GREEN_APPLE_SEEDS = register("green_apple_seeds", new AliasedBlockItem(UBlocks.GREEN_APPLE_SPROUT, new Item.Settings()), ItemGroups.NATURAL); Item SWEET_APPLE_SEEDS = register("sweet_apple_seeds", new AliasedBlockItem(UBlocks.SWEET_APPLE_SPROUT, new Item.Settings()), ItemGroups.NATURAL); Item SOUR_APPLE_SEEDS = register("sour_apple_seeds", new AliasedBlockItem(UBlocks.SOUR_APPLE_SPROUT, new Item.Settings()), ItemGroups.NATURAL); + Item GOLDEN_OAK_SEEDS = register("golden_oak_seeds", new AliasedBlockItem(UBlocks.GOLDEN_OAK_SPROUT, new Item.Settings()), ItemGroups.NATURAL); Item MUG = register("mug", new Item(new Settings().maxCount(16)), ItemGroups.TOOLS); Item CIDER = register("cider", new DrinkableItem(new Item.Settings().food(UFoodComponents.CIDER).maxCount(1).recipeRemainder(MUG)), ItemGroups.FOOD_AND_DRINK); @@ -161,6 +164,7 @@ public interface UItems { Item PALM_BASKET = register("palm_basket", new BasketItem(AirBalloonEntity.BasketType.of(UWoodTypes.PALM_BOAT_TYPE), new Item.Settings().maxCount(1)), ItemGroups.TOOLS); Item GIANT_BALLOON = register("giant_balloon", new HotAirBalloonItem(new Item.Settings().maxCount(1)), ItemGroups.TOOLS); + Item SPECTRAL_CLOCK = register("spectral_clock", new Item(new Item.Settings()), ItemGroups.TOOLS); Item LIGHT_GRAY_BED_SHEETS = register(CloudBedBlock.SheetPattern.LIGHT_GRAY); Item GRAY_BED_SHEETS = register(CloudBedBlock.SheetPattern.GRAY); diff --git a/src/main/java/com/minelittlepony/unicopia/item/URecipes.java b/src/main/java/com/minelittlepony/unicopia/item/URecipes.java index 102690d0..53494d38 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/URecipes.java +++ b/src/main/java/com/minelittlepony/unicopia/item/URecipes.java @@ -32,6 +32,7 @@ public interface URecipes { RecipeType SPELLBOOK = RecipeType.register("unicopia:spellbook"); RecipeType CLOUD_SHAPING = RecipeType.register("unicopia:cloud_shaping"); + RecipeType GROWING = RecipeType.register("unicopia:growing"); RecipeSerializer ZAP_APPLE_SERIALIZER = RecipeSerializer.register("unicopia:crafting_zap_apple", new ZapAppleRecipe.Serializer()); RecipeSerializer GLOWING_SERIALIZER = RecipeSerializer.register("unicopia:crafting_glowing", new SpecialRecipeSerializer<>(GlowingRecipe::new)); @@ -42,6 +43,7 @@ public interface URecipes { RecipeSerializer TRAIT_COMBINING = RecipeSerializer.register("unicopia:spellbook/combining", new SpellEnhancingRecipe.Serializer()); RecipeSerializer SPELL_DUPLICATING = RecipeSerializer.register("unicopia:spellbook/duplicating", new SpellDuplicatingRecipe.Serializer()); RecipeSerializer CLOUD_SHAPING_SERIALIZER = RecipeSerializer.register("unicopia:cloud_shaping", new CuttingRecipe.Serializer<>(CloudShapingRecipe::new) {}); + RecipeSerializer TRANSFORM_CROP_SERIALIZER = RecipeSerializer.register("unicopia:transform_crop", new TransformCropsRecipe.Serializer()); static void bootstrap() { LootTableEvents.MODIFY.register((res, manager, id, supplier, setter) -> { diff --git a/src/main/java/com/minelittlepony/unicopia/item/WearableItem.java b/src/main/java/com/minelittlepony/unicopia/item/WearableItem.java index 23150589..3ac89427 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/WearableItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/WearableItem.java @@ -49,6 +49,7 @@ public abstract class WearableItem extends Item implements Equipment { return ArmorMaterials.LEATHER.getEquipSound(); } + @Deprecated @Override public final EquipmentSlot getSlotType() { return getSlotType(getDefaultStack()); diff --git a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudBlockItem.java b/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudBlockItem.java index 4652d8e9..f208e723 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudBlockItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudBlockItem.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.item.cloud; +import com.minelittlepony.unicopia.InteractionManager; + import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; @@ -30,7 +32,7 @@ extends BlockItem { @Override public TypedActionResult use(World world, PlayerEntity user, Hand hand) { - + InteractionManager.instance().sendPlayerLookAngles(user); Vec3d targetPos = user.getEyePos().add(user.getRotationVec(1).multiply(1, 1.5, 1).normalize().multiply(2)); ItemPlacementContext context = new ItemPlacementContext(user, hand, user.getStackInHand(hand), new BlockHitResult( targetPos, diff --git a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java b/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java index 048f98e1..962b303b 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java +++ b/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java @@ -14,6 +14,11 @@ public class CloudShapingRecipe extends StonecuttingRecipe { super(group, input, output); } + @Override + public boolean isIgnoredInRecipeBook() { + return true; + } + @Override public RecipeType getType() { return URecipes.CLOUD_SHAPING; diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/ConsumptionEnchantment.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/ConsumptionEnchantment.java index 547710c0..9976f03b 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/enchantment/ConsumptionEnchantment.java +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/ConsumptionEnchantment.java @@ -19,7 +19,6 @@ import net.minecraft.item.ItemStack; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; public class ConsumptionEnchantment extends SimpleEnchantment { @@ -44,7 +43,7 @@ public class ConsumptionEnchantment extends SimpleEnchantment { Block.getDroppedStacks(state, world, pos, blockEntity, entity, tool).forEach(s -> { world.playSound(null, pos, USounds.ENCHANTMENT_CONSUMPTION_CONSUME, SoundCategory.BLOCKS, 0.05F, (float)world.random.nextTriangular(0.6F, 0.2F)); - ExperienceOrbEntity.spawn(world, Vec3d.ofCenter(pos).add(VecHelper.supply(vecComponentFactory)), s.getCount()); + ExperienceOrbEntity.spawn(world, pos.toCenterPos().add(VecHelper.supply(vecComponentFactory)), s.getCount()); UCriteria.USE_CONSUMPTION.trigger(entity); }); state.onStacksDropped(world, pos, tool, true); diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinFlowableFluid.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinFlowableFluid.java index dff7a8f9..14a9a84d 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinFlowableFluid.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinFlowableFluid.java @@ -5,7 +5,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.ability.magic.spell.effect.HydrophobicSpell; import net.minecraft.block.BlockState; @@ -17,7 +16,7 @@ import net.minecraft.world.BlockView; abstract class MixinFlowableFluid { @Inject(method = "canFill", at = @At("HEAD"), cancellable = true) private void onCanFill(BlockView world, BlockPos pos, BlockState state, Fluid fluid, CallbackInfoReturnable info) { - if (InteractionManager.instance().getCasterView(world).filter(view -> HydrophobicSpell.blocksFluidFlow(view, pos, fluid.getDefaultState())).isPresent()) { + if (HydrophobicSpell.blocksFluidFlow(world, pos, fluid.getDefaultState())) { info.setReturnValue(false); } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinArmorFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinArmorFeatureRenderer.java index be01f778..2e6cc68b 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinArmorFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinArmorFeatureRenderer.java @@ -1,19 +1,9 @@ package com.minelittlepony.unicopia.mixin.client; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer; - -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRenderer; -import net.minecraft.client.render.entity.feature.FeatureRendererContext; import net.minecraft.client.render.entity.model.BipedEntityModel; -import net.minecraft.client.render.model.BakedModelManager; -import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.LivingEntity; @Mixin(ArmorFeatureRenderer.class) @@ -21,13 +11,13 @@ abstract class MixinArmorFeatureRenderer< T extends LivingEntity, M extends BipedEntityModel, A extends BipedEntityModel> - extends FeatureRenderer implements AccessoryFeatureRenderer.FeatureRoot { + extends FeatureRenderer/* implements AccessoryFeatureRenderer.FeatureRoot*/ { - private AccessoryFeatureRenderer accessories; + //private AccessoryFeatureRenderer accessories; MixinArmorFeatureRenderer() { super(null); } - @Override + /*@Override public AccessoryFeatureRenderer getAccessories() { return accessories; } @@ -40,5 +30,5 @@ abstract class MixinArmorFeatureRenderer< @Inject(method = "render", at = @At("RETURN")) private void onRender(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, T entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch, CallbackInfo info) { getAccessories().render(stack, renderContext, lightUv, entity, limbDistance, limbAngle, tickDelta, age, headYaw, headPitch); - } + }*/ } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinLivingEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinLivingEntityRenderer.java index 3e153574..6f915b70 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinLivingEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinLivingEntityRenderer.java @@ -1,17 +1,25 @@ package com.minelittlepony.unicopia.mixin.client; +import java.util.List; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer; import com.minelittlepony.unicopia.client.render.AnimalPoser; import com.minelittlepony.unicopia.client.render.PlayerPoser; +import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer.FeatureRoot; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.LivingEntityRenderer; +import net.minecraft.client.render.entity.feature.FeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRendererContext; import net.minecraft.client.render.entity.model.BipedEntityModel; import net.minecraft.client.render.entity.model.EntityModel; @@ -21,8 +29,31 @@ import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.player.PlayerEntity; @Mixin(LivingEntityRenderer.class) -abstract class MixinLivingEntityRenderer> extends EntityRenderer implements FeatureRendererContext { +abstract class MixinLivingEntityRenderer> extends EntityRenderer + implements FeatureRendererContext, FeatureRoot { + @Shadow + private @Final List> features; + MixinLivingEntityRenderer() { super(null); } + @Nullable + private AccessoryFeatureRenderer accessories; + + @Override + @SuppressWarnings("unchecked") + public AccessoryFeatureRenderer getAccessories() { + if (accessories == null) { + accessories = features.stream() + .filter(a -> a instanceof FeatureRoot) + .map(a -> ((FeatureRoot)a).getAccessories()) + .findFirst() + .orElseGet(() -> { + var feature = new AccessoryFeatureRenderer<>(this); + features.add(feature); + return feature; + }); + } + return accessories; + } @Inject(method = "render", at = @At( @@ -36,6 +67,7 @@ abstract class MixinLivingEntityRenderer)getModel(), PlayerPoser.Context.THIRD_PERSON); } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java new file mode 100644 index 00000000..848ef7d0 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java @@ -0,0 +1,20 @@ +package com.minelittlepony.unicopia.mixin.client; + +import java.util.Queue; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.WorldRenderer; + +@Mixin(MinecraftClient.class) +public interface MixinMinecraftClient { + @Accessor("renderTaskQueue") + Queue getRenderTaskQueue(); + + @Mutable + @Accessor("worldRenderer") + void setWorldRenderer(WorldRenderer worldRenderer); +} diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java index 35503ffd..cd1eac71 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java @@ -1,13 +1,11 @@ package com.minelittlepony.unicopia.mixin.client; -import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer; import com.minelittlepony.unicopia.client.render.PlayerPoser; import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer.FeatureRoot; @@ -21,31 +19,14 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Arm; @Mixin(PlayerEntityRenderer.class) -abstract class MixinPlayerEntityRenderer - extends LivingEntityRenderer> - implements FeatureRoot> { - @Nullable - private AccessoryFeatureRenderer> accessories; - +abstract class MixinPlayerEntityRenderer extends LivingEntityRenderer> { MixinPlayerEntityRenderer() { super(null, null, 0); } - @Override @SuppressWarnings("unchecked") - public AccessoryFeatureRenderer> getAccessories() { - if (accessories == null) { - accessories = features.stream() - .filter(a -> a instanceof FeatureRoot) - .map(a -> ((FeatureRoot>)a).getAccessories()) - .findFirst() - .orElseGet(() -> new AccessoryFeatureRenderer<>(this)); - } - return accessories; - } - @Inject(method = "renderArm", at = @At("RETURN")) private void onRenderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, AbstractClientPlayerEntity player, ModelPart arm, ModelPart sleeve, CallbackInfo info) { Arm a = this.getModel().leftArm == arm ? Arm.LEFT : Arm.RIGHT; - getAccessories().renderArm(matrices, vertexConsumers, light, player, arm, a); + ((FeatureRoot>)this).getAccessories().renderArm(matrices, vertexConsumers, light, player, arm, a); } @Inject(method = "renderArm", diff --git a/src/main/java/com/minelittlepony/unicopia/network/Channel.java b/src/main/java/com/minelittlepony/unicopia/network/Channel.java index 48592fa2..67f56df1 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/Channel.java +++ b/src/main/java/com/minelittlepony/unicopia/network/Channel.java @@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.network; import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.server.world.UnicopiaWorldProperties; +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; import com.sollace.fabwork.api.packets.*; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; @@ -29,11 +30,12 @@ public interface Channel { S2CPacketType> SERVER_SPELLBOOK_UPDATE = SimpleNetworking.serverToClient(Unicopia.id("server_spellbook_update"), MsgSpellbookStateChanged::new); C2SPacketType> CLIENT_SPELLBOOK_UPDATE = SimpleNetworking.clientToServer(Unicopia.id("client_spellbook_update"), MsgSpellbookStateChanged::new); - S2CPacketType SERVER_RESOURCES_SEND = SimpleNetworking.serverToClient(Unicopia.id("resources_send"), MsgServerResources::new); + S2CPacketType SERVER_RESOURCES = SimpleNetworking.serverToClient(Unicopia.id("resources"), MsgServerResources::new); S2CPacketType SERVER_OTHER_PLAYER_CAPABILITIES = SimpleNetworking.serverToClient(Unicopia.id("other_player_capabilities"), MsgOtherPlayerCapabilities::new); S2CPacketType SERVER_PLAYER_ANIMATION_CHANGE = SimpleNetworking.serverToClient(Unicopia.id("other_player_animation_change"), MsgPlayerAnimationChange::new); S2CPacketType SERVER_SKY_ANGLE = SimpleNetworking.serverToClient(Unicopia.id("sky_angle"), MsgSkyAngle::new); + S2CPacketType SERVER_ZAP_STAGE = SimpleNetworking.serverToClient(Unicopia.id("zap_stage"), MsgZapAppleStage::new); static void bootstrap() { ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { @@ -50,8 +52,10 @@ public interface Channel { Unicopia.LOGGER.info("Setting {}'s race to {} due to host setting", handler.player.getDisplayName().getString(), Race.REGISTRY.getId(race).toString()); } } - sender.sendPacket(SERVER_RESOURCES_SEND.id(), new MsgServerResources().toBuffer()); + sender.sendPacket(SERVER_RESOURCES.id(), new MsgServerResources().toBuffer()); sender.sendPacket(SERVER_SKY_ANGLE.id(), new MsgSkyAngle(UnicopiaWorldProperties.forWorld(handler.getPlayer().getServerWorld()).getTangentalSkyAngle()).toBuffer()); + ZapAppleStageStore store = ZapAppleStageStore.get(handler.player.getServerWorld()); + sender.sendPacket(SERVER_ZAP_STAGE.id(), new MsgZapAppleStage(store.getStage(), store.getStageDelta()).toBuffer()); }); } } diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgRemoveSpell.java b/src/main/java/com/minelittlepony/unicopia/network/MsgRemoveSpell.java index 37b1e0a4..771b2c73 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgRemoveSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgRemoveSpell.java @@ -10,7 +10,7 @@ import net.minecraft.network.PacketByteBuf; import net.minecraft.server.network.ServerPlayerEntity; /** - * Sent to the server when a player activates an ability. + * Sent to the server when a player dismisses a spell from their dismiss spell screen */ public record MsgRemoveSpell (UUID id) implements HandledPacket { MsgRemoveSpell(PacketByteBuf buffer) { @@ -30,7 +30,7 @@ public record MsgRemoveSpell (UUID id) implements HandledPacket spell.getUuid().equals(id), true); + player.getSpellSlot().remove(id); } } } diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgZapAppleStage.java b/src/main/java/com/minelittlepony/unicopia/network/MsgZapAppleStage.java new file mode 100644 index 00000000..2a63a8d7 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgZapAppleStage.java @@ -0,0 +1,22 @@ +package com.minelittlepony.unicopia.network; + +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; +import com.sollace.fabwork.api.packets.Packet; + +import net.minecraft.network.PacketByteBuf; + +public record MsgZapAppleStage ( + ZapAppleStageStore.Stage stage, + long delta + ) implements Packet { + + public MsgZapAppleStage(PacketByteBuf buffer) { + this(buffer.readEnumConstant(ZapAppleStageStore.Stage.class), buffer.readLong()); + } + + @Override + public void toBuffer(PacketByteBuf buffer) { + buffer.writeEnumConstant(stage); + buffer.writeLong(delta); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java index f54aebaa..83507e6b 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java +++ b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java @@ -44,7 +44,13 @@ public class EffectSync implements SpellContainer, NbtSerialisable { } public boolean tick(Situation situation) { - return tick(spell -> Operation.ofBoolean(spell.tick(owner, situation))); + return tick(spell -> { + if (spell.isDying()) { + spell.tickDying(owner); + return Operation.ofBoolean(!spell.isDead()); + } + return Operation.ofBoolean(spell.tick(owner, situation)); + }); } public boolean tick(Function tickAction) { @@ -87,6 +93,16 @@ public class EffectSync implements SpellContainer, NbtSerialisable { } } + @Override + public void remove(UUID id) { + Spell spell = spells.getReference(id); + spell.setDead(); + spell.tickDying(owner); + if (spell.isDead()) { + spells.removeReference(id); + } + } + @Override public boolean removeWhere(Predicate test, boolean update) { return reduce(update, (initial, effect) -> { @@ -136,7 +152,7 @@ public class EffectSync implements SpellContainer, NbtSerialisable { @SuppressWarnings("unchecked") private Stream read(@Nullable SpellPredicate type, boolean synchronize, boolean sendUpdate) { if (synchronize && spells.fromNbt(owner.asEntity().getDataTracker().get(param)) && sendUpdate) { - owner.asEntity().getDataTracker().set(param, spells.toNbt()); + write(); } if (type == null) { diff --git a/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java b/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java index df54150f..90582e52 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java +++ b/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java @@ -83,7 +83,13 @@ public class NetworkedReferenceSet { } } - private synchronized void removeReference(UUID id) { + @Nullable + synchronized T getReference(UUID id) { + NetworkedReference i = values.get(id); + return i == null ? null : i.getReference().orElse(null); + } + + synchronized void removeReference(UUID id) { dirty |= ids.remove(id); NetworkedReference i = values.remove(id); if (i != null) { diff --git a/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java b/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java index 889191a3..586c89fa 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java +++ b/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java @@ -36,8 +36,9 @@ public class ClientNetworkHandlerImpl { Channel.SERVER_BLOCK_DESTRUCTION.receiver().addPersistentListener(this::handleBlockDestruction); Channel.CANCEL_PLAYER_ABILITY.receiver().addPersistentListener(this::handleCancelAbility); Channel.UNLOCK_TRAITS.receiver().addPersistentListener(this::handleUnlockTraits); - Channel.SERVER_RESOURCES_SEND.receiver().addPersistentListener(this::handleServerResources); + Channel.SERVER_RESOURCES.receiver().addPersistentListener(this::handleServerResources); Channel.SERVER_SKY_ANGLE.receiver().addPersistentListener(this::handleSkyAngle); + Channel.SERVER_ZAP_STAGE.receiver().addPersistentListener(this::handleZapStage); Channel.SERVER_PLAYER_ANIMATION_CHANGE.receiver().addPersistentListener(this::handlePlayerAnimation); Channel.SERVER_REQUEST_PLAYER_LOOK.receiver().addPersistentListener(this::handleCasterLookRequest); } @@ -94,6 +95,10 @@ public class ClientNetworkHandlerImpl { UnicopiaClient.getInstance().tangentalSkyAngle.update(packet.tangentalSkyAngle(), 200); } + private void handleZapStage(PlayerEntity sender, MsgZapAppleStage packet) { + UnicopiaClient.getInstance().setZapAppleStage(packet.stage(), packet.delta()); + } + @SuppressWarnings("unchecked") private void handleServerResources(PlayerEntity sender, MsgServerResources packet) { SpellTraits.load(packet.traits()); diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java deleted file mode 100644 index 1fc12c06..00000000 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.minelittlepony.unicopia.particle; - -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.WeakHashMap; -import java.util.function.Consumer; - -import com.minelittlepony.unicopia.EntityConvertable; -import com.minelittlepony.unicopia.ability.magic.Caster; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.particle.Particle; -import net.minecraft.world.World; - -/** - * A connection class for updating and persisting an attached particle effect. - */ -public class ParticleHandle { - private final Map loadedEffects = new WeakHashMap<>(); - - public Optional update(UUID id, ParticleSource source, Consumer constructor) { - return update(id, "prime", source, constructor); - } - - public Optional update(UUID id, String partName, ParticleSource source, Consumer constructor) { - return get(partName).or(() -> { - if (source.asEntity().getWorld().isClient) { - new ClientHandle().addParticle(id, partName, source, constructor); - } - return get(partName); - }); - } - - public void destroy() { - loadedEffects.values().forEach(Attachment::detach); - loadedEffects.clear(); - } - - private Optional get(String partName) { - return Optional.ofNullable(loadedEffects.get(partName)).filter(Attachment::isStillAlive); - } - - private final class ClientHandle { - private static final Map> SPAWNED_PARTICLES = new HashMap<>(); - - private Particle pp; - - @Environment(EnvType.CLIENT) - private void addParticle(UUID id, String partName, ParticleSource source, Consumer constructor) { - SPAWNED_PARTICLES.values().removeIf(set -> { - set.values().removeIf(particle -> particle.get() == null); - return set.isEmpty(); - }); - - Entry p = SPAWNED_PARTICLES.computeIfAbsent(id, i -> new WeakHashMap<>()).computeIfAbsent(partName, i -> { - constructor.accept((effect, pos, vel) -> { - pp = MinecraftClient.getInstance().particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z); - if (pp instanceof Attachment) { - ((Attachment) pp).attach(new Link(id, source)); - } - }); - return new Entry(new WeakReference<>(MinecraftClient.getInstance().world), new WeakReference<>(pp)); - }); - - if (p.get() instanceof Attachment) { - loadedEffects.put(partName, (Attachment)p.get()); - } - } - - record Entry (WeakReference world, WeakReference particle) { - public Particle get() { - if (world.get() == null || world.get() != MinecraftClient.getInstance().world) { - return null; - } - - Particle particle = this.particle.get(); - - return particle == null || !particle.isAlive() ? null : particle; - } - } - } - - public interface Attachment { - int ATTR_RADIUS = 0; - int ATTR_COLOR = 1; - int ATTR_OPACITY = 2; - int ATTR_PITCH = 3; - int ATTR_YAW = 4; - int ATTR_BOUND = 5; - - boolean isStillAlive(); - - void attach(Link link); - - void detach(); - - void setAttribute(int key, Number value); - } - - public static final class Link { - private Optional>> caster = Optional.empty(); - private UUID effect; - - private Link(UUID effect, EntityConvertable caster) { - this.caster = Optional.of(new WeakReference<>(caster)); - this.effect = effect; - } - - public Optional> get() { - caster = caster.filter(r -> r.get() != null && (!(r.get() instanceof Caster c) || c.getSpellSlot().contains(effect)) && r.get().asEntity().isAlive()); - return caster.map(WeakReference::get); - } - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleSpawner.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleSpawner.java index e19436e2..f1adf5a0 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleSpawner.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/ParticleSpawner.java @@ -4,5 +4,7 @@ import net.minecraft.particle.ParticleEffect; import net.minecraft.util.math.Vec3d; public interface ParticleSpawner { + ParticleSpawner EMPTY = (effect, pos, vel) -> {}; + void addParticle(ParticleEffect effect, Vec3d position, Vec3d velocity); } diff --git a/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java b/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java new file mode 100644 index 00000000..15171015 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java @@ -0,0 +1,55 @@ +package com.minelittlepony.unicopia.particle; + + +import java.util.Locale; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import net.minecraft.entity.Entity; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.particle.ParticleType; +import net.minecraft.registry.Registries; + +public class TargetBoundParticleEffect implements ParticleEffect { + @SuppressWarnings("deprecation") + public static final Factory FACTORY = ParticleFactoryHelper.of(TargetBoundParticleEffect::new, TargetBoundParticleEffect::new); + + private final ParticleType type; + private final int targetId; + + protected TargetBoundParticleEffect(ParticleType type, StringReader reader) throws CommandSyntaxException { + this.type = type; + this.targetId = -1; + } + + protected TargetBoundParticleEffect(ParticleType type, PacketByteBuf buf) { + this.type = type; + this.targetId = buf.readInt(); + } + + public TargetBoundParticleEffect(ParticleType type, Entity target) { + this.type = type; + this.targetId = target.getId(); + } + + public int getTargetId() { + return targetId; + } + + @Override + public ParticleType getType() { + return type; + } + + @Override + public void write(PacketByteBuf buf) { + buf.writeInt(targetId); + } + + @Override + public String asString() { + return String.format(Locale.ROOT, "%s", Registries.PARTICLE_TYPE.getId(getType()), targetId); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java index 8d5208f0..9bec2e5c 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java @@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.particle; import com.minelittlepony.unicopia.Unicopia; import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes; +import net.minecraft.particle.BlockStateParticleEffect; import net.minecraft.particle.DefaultParticleType; import net.minecraft.particle.ParticleType; import net.minecraft.registry.Registry; @@ -12,10 +13,13 @@ public interface UParticles { ParticleType UNICORN_MAGIC = register("unicorn_magic", FabricParticleTypes.complex(MagicParticleEffect.FACTORY)); DefaultParticleType CHANGELING_MAGIC = register("changeling_magic", FabricParticleTypes.simple()); + DefaultParticleType BUBBLE = register("bubble", FabricParticleTypes.simple()); + ParticleType DUST_CLOUD = register("dust_cloud", FabricParticleTypes.complex(BlockStateParticleEffect.PARAMETERS_FACTORY)); ParticleType RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); - DefaultParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.simple()); + ParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY)); + @Deprecated ParticleType MAGIC_RUNES = register("magic_runes", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple()); diff --git a/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java b/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java index df9cada9..61e33202 100644 --- a/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.projectile; +import java.util.Optional; + import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; @@ -19,6 +21,7 @@ import net.minecraft.entity.LivingEntity; import net.minecraft.entity.MovementType; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.damage.DamageSources; +import net.minecraft.entity.damage.DamageType; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; @@ -27,9 +30,13 @@ import net.minecraft.entity.projectile.PersistentProjectileEntity; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.BlockTags; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; +import net.minecraft.util.Identifier; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.math.BlockPos; @@ -46,6 +53,8 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl private int inWaterTime; + private RegistryKey damageType = UDamageTypes.ROCK; + public PhysicsBodyProjectileEntity(EntityType type, World world, ItemStack stack) { super(type, world, stack); } @@ -74,6 +83,10 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl return getDataTracker().get(ITEM); } + public void setDamageType(RegistryKey damageType) { + this.damageType = damageType; + } + @Override protected ItemStack asItemStack() { return getStack(); @@ -149,7 +162,7 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl return new DamageSources(getWorld().getRegistryManager()) { @Override public DamageSource arrow(PersistentProjectileEntity source, @Nullable Entity attacker) { - return create(UDamageTypes.ROCK, source, attacker); + return create(damageType, source, attacker); } }; } @@ -262,11 +275,17 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl if (!stack.isEmpty()) { nbt.put("Item", stack.writeNbt(new NbtCompound())); } + nbt.putString("damageType", damageType.getValue().toString()); } @Override public void readCustomDataFromNbt(NbtCompound nbt) { super.readCustomDataFromNbt(nbt); setStack(ItemStack.fromNbt(nbt.getCompound("Item"))); + if (nbt.contains("damageType", NbtElement.STRING_TYPE)) { + Optional.ofNullable(Identifier.tryParse(nbt.getString("damageType"))).ifPresent(id -> { + setDamageType(RegistryKey.of(RegistryKeys.DAMAGE_TYPE, id)); + }); + } } } diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java b/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java index b6eb0eea..8d2e309c 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java @@ -1,88 +1,93 @@ package com.minelittlepony.unicopia.server.world; +import java.lang.ref.WeakReference; import java.util.*; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.ability.magic.CasterView; import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.util.NbtSerialisable; - import net.minecraft.nbt.*; import net.minecraft.util.Identifier; import net.minecraft.world.PersistentState; import net.minecraft.world.World; -public class Ether extends PersistentState implements CasterView { +public class Ether extends PersistentState { private static final Identifier ID = Unicopia.id("ether"); public static Ether get(World world) { return WorldOverlay.getPersistableStorage(world, ID, Ether::new, Ether::new); } - private final Map> advertisingEndpoints = new HashMap<>(); + private final Map>>> endpoints; private final Object locker = new Object(); private final World world; Ether(World world, NbtCompound compound) { - this(world); - compound.getKeys().forEach(key -> { - Identifier typeId = Identifier.tryParse(key); - if (typeId != null) { - Set uuids = getEntries(typeId); - compound.getList(key, NbtElement.COMPOUND_TYPE).forEach(entry -> { - Entry e = new Entry(); - e.fromNBT((NbtCompound)entry); - uuids.add(e); - }); - } + this.world = world; + this.endpoints = NbtSerialisable.readMap(compound.getCompound("endpoints"), Identifier::tryParse, typeNbt -> { + return NbtSerialisable.readMap((NbtCompound)typeNbt, UUID::fromString, entityNbt -> { + return NbtSerialisable.readMap((NbtCompound)entityNbt, UUID::fromString, Entry::new); + }); }); } Ether(World world) { this.world = world; + this.endpoints = new HashMap<>(); } @Override public NbtCompound writeNbt(NbtCompound compound) { synchronized (locker) { - advertisingEndpoints.forEach((id, uuids) -> { - NbtList list = new NbtList(); - uuids.forEach(uuid -> { - if (uuid.isAlive()) { - list.add(uuid.toNBT()); - } + pruneNodes(); + compound.put("endpoints", NbtSerialisable.writeMap(endpoints, Identifier::toString, entities -> { + return NbtSerialisable.writeMap(entities, UUID::toString, spells -> { + return NbtSerialisable.writeMap(spells, UUID::toString, Entry::toNBT); }); - compound.put(id.toString(), list); - }); - + })); return compound; } } - public Entry put(SpellType spellType, Caster caster) { + @SuppressWarnings("unchecked") + public Entry getOrCreate(T spell, Caster caster) { synchronized (locker) { - var entry = new Entry(caster); - getEntries(spellType.getId()).add(entry); - markDirty(); + Entry entry = (Entry)endpoints + .computeIfAbsent(spell.getType().getId(), typeId -> new HashMap<>()) + .computeIfAbsent(caster.asEntity().getUuid(), entityId -> new HashMap<>()) + .computeIfAbsent(spell.getUuid(), spellid -> { + markDirty(); + return new Entry<>(spell, caster); + }); + if (entry.removed) { + entry.removed = false; + markDirty(); + } + if (entry.spell.get() != spell) { + entry.spell = new WeakReference<>(spell); + markDirty(); + } return entry; } } - public void remove(SpellType spellType, UUID id) { + public void remove(SpellType spellType, UUID entityId) { synchronized (locker) { - Identifier typeId = spellType.getId(); - Set refs = advertisingEndpoints.get(typeId); - if (refs != null) { - refs.removeIf(ref -> ref.isDead() || ref.entity.getTarget().filter(target -> id.equals(target.uuid())).isPresent()); - if (refs.isEmpty()) { - advertisingEndpoints.remove(typeId); + endpoints.computeIfPresent(spellType.getId(), (typeId, entries) -> { + if (entries.remove(entityId) != null) { + markDirty(); } - markDirty(); - } + return entries.isEmpty() ? null : entries; + }); } } @@ -90,72 +95,123 @@ public class Ether extends PersistentState implements CasterView { remove(spellType, caster.asEntity().getUuid()); } - public Set getEntries(SpellType spellType) { - return getEntries(spellType.getId()); + public void remove(T spell, Caster caster) { + Entry entry = get(spell, caster); + if (entry != null) { + entry.markDead(); + } } - private Set getEntries(Identifier typeId) { + @SuppressWarnings("unchecked") + public Entry get(T spell, Caster caster) { + return get((SpellType)spell.getType(), caster.asEntity().getUuid(), spell.getUuid()); + } + + public Entry get(SpellType spell, EntityReference.EntityValues entityId, @Nullable UUID spellId) { + return get(spell, entityId.uuid(), spellId); + } + + @SuppressWarnings("unchecked") + @Nullable + private Entry get(SpellType spell, UUID entityId, @Nullable UUID spellId) { + if (spellId == null) { + return null; + } synchronized (locker) { - return advertisingEndpoints.compute(typeId, (k, old) -> { - if (old == null) { - old = new HashSet<>(); - } else { - old.removeIf(Entry::isDead); + Entry entry = endpoints + .getOrDefault(spell.getId(), Map.of()) + .getOrDefault(entityId, Map.of()) + .get(spellId); + return entry == null || entry.isDead() ? null : (Entry)entry; + } + } + + public boolean anyMatch(SpellType spellType, BiPredicate> condition) { + return anyMatch(spellType, entry -> { + var spell = entry.getSpell(); + var caster = entry.getCaster(); + return spell != null && caster != null && condition.test(spell, caster); + }); + } + + @SuppressWarnings("unchecked") + public boolean anyMatch(SpellType spellType, Predicate> condition) { + synchronized (locker) { + for (var entries : endpoints.getOrDefault(spellType.getId(), Map.of()).values()) { + for (var entry : entries.values()) { + if (!entry.isDead() && condition.test((Entry)entry)) { + return true; + } } - return old; + } + } + return false; + } + + private void pruneNodes() { + this.endpoints.values().removeIf(entities -> { + entities.values().removeIf(spells -> { + spells.values().removeIf(Entry::isDead); + return spells.isEmpty(); }); - } + return entities.isEmpty(); + }); } - public Optional getEntry(SpellType spellType, Caster caster) { - synchronized (locker) { - return getEntries(spellType).stream().filter(e -> e.entity.referenceEquals(caster.asEntity())).findFirst(); - } - } - - public Optional getEntry(SpellType spellType, UUID uuid) { - synchronized (locker) { - return getEntries(spellType).stream().filter(e -> e.equals(uuid)).findFirst(); - } - } - - @Override - public World getWorld() { - return world; - } - - public class Entry implements NbtSerialisable { + public class Entry implements NbtSerialisable { public final EntityReference entity; + + @Nullable + private UUID spellId; + private WeakReference spell; + private boolean removed; private boolean taken; public float pitch; public float yaw; + public float radius; - public Entry() { - entity = new EntityReference<>(); + private Entry(NbtElement nbt) { + this.entity = new EntityReference<>(); + this.spell = new WeakReference<>(null); + this.fromNBT((NbtCompound)nbt); } - public Entry(Caster caster) { - entity = new EntityReference<>(caster.asEntity()); + public Entry(T spell, Caster caster) { + this.entity = new EntityReference<>(caster.asEntity()); + this.spell = new WeakReference<>(spell); + spellId = spell.getUuid(); } boolean isAlive() { - return !removed; + return !isDead(); } boolean isDead() { + if (!removed) { + getSpell(); + } return removed; } + @Nullable + public UUID getSpellId() { + return spellId; + } + public void markDead() { Unicopia.LOGGER.debug("Marking " + entity.getTarget().orElse(null) + " as dead"); removed = true; markDirty(); } + public boolean entityMatches(UUID uuid) { + return entity.getTarget().filter(target -> uuid.equals(target.uuid())).isPresent(); + } + public boolean isAvailable() { - return !removed && !taken; + return !isDead() && !taken; } public void setTaken(boolean taken) { @@ -163,6 +219,43 @@ public class Ether extends PersistentState implements CasterView { markDirty(); } + @Nullable + public T getSpell() { + if (removed) { + return null; + } + T spell = this.spell.get(); + if (spell == null) { + if (spellId != null) { + spell = entity + .getOrEmpty(world) + .flatMap(Caster::of) + .flatMap(caster -> caster.getSpellSlot().get(s -> s.getUuid().equals(spellId), true)) + .orElse(null); + + if (spell != null) { + this.spell = new WeakReference<>(spell); + } + } + } + + if (spell != null && spell.isDead()) { + spellId = null; + spell = null; + markDead(); + } + + return spell; + } + + @Nullable + public Caster getCaster() { + if (removed) { + return null; + } + return Caster.of(this.entity.get(world)).orElse(null); + } + @Override public void toNBT(NbtCompound compound) { entity.toNBT(compound); @@ -170,6 +263,10 @@ public class Ether extends PersistentState implements CasterView { compound.putBoolean("taken", taken); compound.putFloat("pitch", pitch); compound.putFloat("yaw", yaw); + compound.putFloat("radius", radius); + if (spellId != null) { + compound.putUuid("spellId", spellId); + } } @Override @@ -179,20 +276,24 @@ public class Ether extends PersistentState implements CasterView { taken = compound.getBoolean("taken"); pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); + radius = compound.getFloat("radius"); + spellId = compound.containsUuid("spellid") ? compound.getUuid("spellId") : null; } @Override public boolean equals(Object other) { - return other instanceof Entry e && e.entity.referenceEquals(entity); + return other instanceof Entry e + && e.entity.referenceEquals(entity) + && Objects.equals(e.spell.get(), spell.get()); } - public boolean equals(UUID uuid) { - return entity.referenceEquals(uuid); + public boolean equals(UUID entityId, UUID spellId) { + return entity.referenceEquals(entityId) && spellId.equals(this.spellId); } @Override public int hashCode() { - return entity.hashCode(); + return Objects.hash(entity, spell.get()); } } } diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java b/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java index bb55a235..0d456e02 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java @@ -24,7 +24,7 @@ import net.minecraft.world.gen.trunk.UpwardsBranchingTrunkPlacer; public interface UTreeGen { Tree ZAP_APPLE_TREE = Tree.Builder.create(Unicopia.id("zap_apple_tree"), new UpwardsBranchingTrunkPlacer( - 7, 2, 3, + 5, 3, 0, UniformIntProvider.create(3, 6), 0.3f, UniformIntProvider.create(1, 3), @@ -46,6 +46,16 @@ public interface UTreeGen { Tree GREEN_APPLE_TREE = createAppleTree("green_apple", UBlocks.GREEN_APPLE_LEAVES, 2); Tree SWEET_APPLE_TREE = createAppleTree("sweet_apple", UBlocks.SWEET_APPLE_LEAVES, 3); Tree SOUR_APPLE_TREE = createAppleTree("sour_apple", UBlocks.SOUR_APPLE_LEAVES, 5); + Tree GOLDEN_APPLE_TREE = Tree.Builder.create(Unicopia.id("golden_oak_tree"), + new StraightTrunkPlacer(6, 1, 3), + new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3) + ) + .configure(TreeFeatureConfig.Builder::forceDirt) + .farmingCondition(1, 3, 5) + .log(UBlocks.GOLDEN_OAK_LOG) + .leaves(UBlocks.GOLDEN_OAK_LEAVES) + .sapling(Unicopia.id("golden_oak_sapling")) + .build(); Tree BANANA_TREE = Tree.Builder.create(Unicopia.id("banana_tree"), new StraightTrunkPlacer(4, 5, 3), new FernFoliagePlacer(ConstantIntProvider.create(4), ConstantIntProvider.create(0)) @@ -80,10 +90,12 @@ public interface UTreeGen { static Tree createAppleTree(String name, Block leaves, int preferredDensity) { return Tree.Builder.create(Unicopia.id(name + "_tree"), - new StraightTrunkPlacer(4, 6, 2), + new StraightTrunkPlacer(4, 3, 2), new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3) ) .configure(TreeFeatureConfig.Builder::forceDirt) + .biomes(selector -> selector.hasTag(BiomeTags.IS_FOREST)) + .count(2, 0.01F, 1) .farmingCondition(1, preferredDensity - 2, preferredDensity) .log(Blocks.OAK_LOG) .leaves(leaves) diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java b/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java index e1460494..cb27244f 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java @@ -5,13 +5,12 @@ import java.util.stream.StreamSupport; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.block.UBlocks; -import com.minelittlepony.unicopia.block.ZapAppleLeavesBlock; +import com.minelittlepony.unicopia.network.Channel; +import com.minelittlepony.unicopia.network.MsgZapAppleStage; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.util.Tickable; -import net.minecraft.block.BlockState; import net.minecraft.entity.EntityType; import net.minecraft.entity.LightningEntity; import net.minecraft.nbt.*; @@ -23,10 +22,13 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.PersistentState; import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.event.GameEvent; public class ZapAppleStageStore extends PersistentState implements Tickable { private static final Identifier ID = Unicopia.id("zap_apple_stage"); + static final long DAY_LENGTH = World.field_30969; + static final long MOON_PHASES = DimensionType.MOON_SIZES.length; public static ZapAppleStageStore get(World world) { return WorldOverlay.getPersistableStorage(world, ID, ZapAppleStageStore::new, ZapAppleStageStore::new); @@ -35,7 +37,9 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { private final World world; private Stage lastStage = Stage.HIBERNATING; - private int countdown; + private long stageDelta; + private long lastTime; + private boolean stageChanged; private boolean playedMoonEffect; private int nextLightningEvent = 1200; @@ -43,8 +47,8 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { ZapAppleStageStore(World world, NbtCompound compound) { this(world); lastStage = Stage.VALUES[Math.max(0, compound.getInt("stage")) % Stage.VALUES.length]; + stageDelta = compound.getLong("stageDelta"); stageChanged = compound.getBoolean("stageChanged"); - countdown = compound.getInt("countdown"); playedMoonEffect = compound.getBoolean("playedMoonEffect"); nextLightningEvent = compound.getInt("nextLightningEvent"); } @@ -58,32 +62,38 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { if (!world.isDay()) { if (nextLightningEvent > 0) { nextLightningEvent--; - markDirty(); } if (!stageChanged && (lastStage != Stage.HIBERNATING || (world.getMoonPhase() == 0))) { stageChanged = true; - if (countDay()) { - lastStage = lastStage.getNext(); - countdown = 1; - playedMoonEffect = false; - markDirty(); - onStageChanged(); - } + lastStage = lastStage.getNext(); + stageDelta = 0; + playedMoonEffect = false; + sendUpdate(); } } else if (stageChanged) { stageChanged = false; - markDirty(); } - } - private boolean countDay() { + long timeOfDay = world.getTimeOfDay(); + if (stageDelta != 0 && (timeOfDay < lastTime || timeOfDay > lastTime + 10)) { + long timeDifference = timeOfDay - lastTime; + Unicopia.LOGGER.info("Times a changing {}!", timeDifference); + while (timeDifference < 0) { + timeDifference += DAY_LENGTH; + } + stageDelta += timeDifference; + sendUpdate(); + } + lastTime = timeOfDay; + + + stageDelta++; markDirty(); - return countdown-- <= 0; } - protected void onStageChanged() { - //world.setRainGradient(0.5F); + protected void sendUpdate() { + Channel.SERVER_ZAP_STAGE.sendToAllPlayers(new MsgZapAppleStage(getStage(), stageDelta), world); } public void playMoonEffect(BlockPos pos) { @@ -115,7 +125,6 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { /** * Returns true during nights that the zap apples must change their states. - * @return */ public boolean hasStageChanged() { return stageChanged; @@ -128,56 +137,67 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { return lastStage; } + public long getStageDelta() { + return stageDelta; + } + + public float getStageProgress() { + return getStage().getStageProgress(getStageDelta()); + } + + public float getCycleProgress() { + return getStage().getCycleProgress(getStageDelta()); + } + @Override public NbtCompound writeNbt(NbtCompound compound) { compound.putInt("stage", lastStage.ordinal()); + compound.putLong("stageDelta", stageDelta); compound.putBoolean("stageChanged", stageChanged); - compound.putInt("countdown", countdown); compound.putBoolean("playedMoonEffect", playedMoonEffect); compound.putInt("nextLightningEvent", nextLightningEvent); return compound; } public enum Stage implements StringIdentifiable { - HIBERNATING, - GREENING, - FLOWERING, - FRUITING, - RIPE; + HIBERNATING(MOON_PHASES * DAY_LENGTH), + GREENING(DAY_LENGTH), + FLOWERING(DAY_LENGTH), + FRUITING(DAY_LENGTH), + RIPE(DAY_LENGTH); - static final long DAY_LENGTH = 24000; static final Stage[] VALUES = values(); + private final long duration; + + Stage(long duration) { + this.duration = duration; + } + public Stage getNext() { return byId((ordinal() + 1) % VALUES.length); } + public Stage getPrevious() { + return byId(((ordinal() - 1) + VALUES.length) % VALUES.length); + } + + public float getStageProgress(long time) { + return (time % duration) / (float)duration; + } + + public float getCycleProgress(long time) { + float ordinal = ordinal(); + return MathHelper.lerp(getStageProgress(time), ordinal, ordinal + 1) / 5F; + } + public static Stage byId(int id) { return VALUES[MathHelper.clamp(id, 0, VALUES.length)]; } - public boolean mustChangeInto(Stage to) { - return this != to && (getNext() == to || this == HIBERNATING || to == HIBERNATING); - } - - public boolean mustChangeIntoInstantly(Stage to) { - return this != to && (this == HIBERNATING || to == HIBERNATING); - } - - public BlockState getNewState(BlockState currentState) { - if (this == ZapAppleStageStore.Stage.HIBERNATING) { - return UBlocks.ZAP_LEAVES_PLACEHOLDER.getDefaultState(); - } - if (this == ZapAppleStageStore.Stage.FLOWERING) { - return UBlocks.FLOWERING_ZAP_LEAVES.getDefaultState(); - } - return UBlocks.ZAP_LEAVES.getDefaultState().with(ZapAppleLeavesBlock.STAGE, this); - } - @Override public String asString() { return name().toLowerCase(Locale.ROOT); } } - } diff --git a/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java b/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java index 7f40eb69..a346c460 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java +++ b/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java @@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.util; import java.util.*; import java.util.function.*; +import java.util.stream.Collectors; import java.util.stream.Stream; import com.mojang.datafixers.util.Pair; @@ -65,6 +66,18 @@ public interface NbtSerialisable { return parent; } + static Map readMap(NbtCompound nbt, Function keyFunction, Function valueFunction) { + return nbt.getKeys().stream().collect(Collectors.toMap(keyFunction, k -> valueFunction.apply(nbt.get(k)))); + } + + static NbtCompound writeMap(Map map, Function keyFunction, Function valueFunction) { + NbtCompound nbt = new NbtCompound(); + map.forEach((k, v) -> { + nbt.put(keyFunction.apply(k), valueFunction.apply(v)); + }); + return nbt; + } + interface Serializer { T read(NbtCompound compound); diff --git a/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java b/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java index 7ebf61af..c151b47c 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java +++ b/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.util.shape; +import org.spongepowered.include.com.google.common.base.Objects; + import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.random.Random; @@ -102,6 +104,19 @@ public class Sphere implements Shape { return stretch.multiply(rad); } + @Override + public boolean equals(Object other) { + return other instanceof Sphere o + && Objects.equal(stretch, o.stretch) + && hollow == o.hollow + && Double.compare(rad, o.rad) == 0; + } + + @Override + public int hashCode() { + return Objects.hashCode(stretch, hollow, rad); + } + public static double computeEllipsoidArea(double rad, Vec3d stretch) { double p = 1.6075; double result = Math.pow(rad * stretch.x, p) * Math.pow(rad * stretch.y, p); diff --git a/src/main/resources/assets/unicopia/blockstates/curing_joke.json b/src/main/resources/assets/unicopia/blockstates/curing_joke.json new file mode 100644 index 00000000..20ea8e38 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/curing_joke.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "unicopia:block/curing_joke" } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/gold_root.json b/src/main/resources/assets/unicopia/blockstates/gold_root.json new file mode 100644 index 00000000..6791a03a --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/gold_root.json @@ -0,0 +1,12 @@ +{ + "variants": { + "age=0": { "model": "unicopia:block/gold_root_stage0" }, + "age=1": { "model": "unicopia:block/gold_root_stage0" }, + "age=2": { "model": "unicopia:block/gold_root_stage1" }, + "age=3": { "model": "unicopia:block/gold_root_stage1" }, + "age=4": { "model": "unicopia:block/gold_root_stage2" }, + "age=5": { "model": "unicopia:block/gold_root_stage2" }, + "age=6": { "model": "unicopia:block/gold_root_stage2" }, + "age=7": { "model": "unicopia:block/gold_root_stage3" } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/golden_apple.json b/src/main/resources/assets/unicopia/blockstates/golden_apple.json new file mode 100644 index 00000000..e0c679aa --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_apple.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "unicopia:block/golden_apple" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/blockstates/golden_oak_leaves.json b/src/main/resources/assets/unicopia/blockstates/golden_oak_leaves.json new file mode 100644 index 00000000..b8084dfb --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_oak_leaves.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "unicopia:block/golden_oak_leaves" + } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/golden_oak_log.json b/src/main/resources/assets/unicopia/blockstates/golden_oak_log.json new file mode 100644 index 00000000..4f50529f --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_oak_log.json @@ -0,0 +1,16 @@ +{ + "variants": { + "axis=x": { + "model": "unicopia:block/golden_oak_log_horizontal", + "x": 90, + "y": 90 + }, + "axis=y": { + "model": "unicopia:block/golden_oak_log" + }, + "axis=z": { + "model": "unicopia:block/golden_oak_log_horizontal", + "x": 90 + } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/golden_oak_sapling.json b/src/main/resources/assets/unicopia/blockstates/golden_oak_sapling.json new file mode 100644 index 00000000..7288f548 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_oak_sapling.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "unicopia:block/golden_oak_sapling" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/blockstates/golden_oak_sprout.json b/src/main/resources/assets/unicopia/blockstates/golden_oak_sprout.json new file mode 100644 index 00000000..f88ff7ea --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_oak_sprout.json @@ -0,0 +1,28 @@ +{ + "variants": { + "age=0": { + "model": "unicopia:block/apple_sprout_stage0" + }, + "age=1": { + "model": "unicopia:block/apple_sprout_stage1" + }, + "age=2": { + "model": "unicopia:block/apple_sprout_stage2" + }, + "age=3": { + "model": "unicopia:block/apple_sprout_stage3" + }, + "age=4": { + "model": "unicopia:block/apple_sprout_stage4" + }, + "age=5": { + "model": "unicopia:block/apple_sprout_stage5" + }, + "age=6": { + "model": "unicopia:block/apple_sprout_stage6" + }, + "age=7": { + "model": "unicopia:block/apple_sprout_stage7" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/blockstates/hay_block.json b/src/main/resources/assets/unicopia/blockstates/hay_block.json new file mode 100644 index 00000000..1378545e --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/hay_block.json @@ -0,0 +1,31 @@ +{ + "multipart": [ + { "apply": { "model": "unicopia:block/hay_bale_bne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_east": true } }, + + { "apply": { "model": "unicopia:block/hay_bale_bne" }, "when": { "axis": "y", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bnw" }, "when": { "axis": "y", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bse" }, "when": { "axis": "y", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bsw" }, "when": { "axis": "y", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tne" }, "when": { "axis": "y", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tnw" }, "when": { "axis": "y", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tse" }, "when": { "axis": "y", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tsw" }, "when": { "axis": "y", "top_south_west": true } }, + + + { "apply": { "model": "unicopia:block/hay_bale_bne", "x": 90 }, "when": { "axis": "z", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bnw", "x": 90 }, "when": { "axis": "z", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bse", "x": 90 }, "when": { "axis": "z", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bsw", "x": 90 }, "when": { "axis": "z", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tne", "x": 90 }, "when": { "axis": "z", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tnw", "x": 90 }, "when": { "axis": "z", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tse", "x": 90 }, "when": { "axis": "z", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tsw", "x": 90 }, "when": { "axis": "z", "top_north_west": true } } + ] +} diff --git a/src/main/resources/assets/unicopia/blockstates/rice_block.json b/src/main/resources/assets/unicopia/blockstates/rice_block.json new file mode 100644 index 00000000..61251408 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/rice_block.json @@ -0,0 +1,31 @@ +{ + "multipart": [ + { "apply": { "model": "unicopia:block/rice_bale_bne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_east": true } }, + + { "apply": { "model": "unicopia:block/rice_bale_bne" }, "when": { "axis": "y", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bnw" }, "when": { "axis": "y", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bse" }, "when": { "axis": "y", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bsw" }, "when": { "axis": "y", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tne" }, "when": { "axis": "y", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tnw" }, "when": { "axis": "y", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tse" }, "when": { "axis": "y", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tsw" }, "when": { "axis": "y", "top_south_west": true } }, + + + { "apply": { "model": "unicopia:block/rice_bale_bne", "x": 90 }, "when": { "axis": "z", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bnw", "x": 90 }, "when": { "axis": "z", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bse", "x": 90 }, "when": { "axis": "z", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bsw", "x": 90 }, "when": { "axis": "z", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tne", "x": 90 }, "when": { "axis": "z", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tnw", "x": 90 }, "when": { "axis": "z", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tse", "x": 90 }, "when": { "axis": "z", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tsw", "x": 90 }, "when": { "axis": "z", "top_north_west": true } } + ] +} diff --git a/src/main/resources/assets/unicopia/blockstates/straw_block.json b/src/main/resources/assets/unicopia/blockstates/straw_block.json new file mode 100644 index 00000000..4f1da006 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/straw_block.json @@ -0,0 +1,31 @@ +{ + "multipart": [ + { "apply": { "model": "unicopia:block/straw_bale_bne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_east": true } }, + + { "apply": { "model": "unicopia:block/straw_bale_bne" }, "when": { "axis": "y", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bnw" }, "when": { "axis": "y", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bse" }, "when": { "axis": "y", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bsw" }, "when": { "axis": "y", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tne" }, "when": { "axis": "y", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tnw" }, "when": { "axis": "y", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tse" }, "when": { "axis": "y", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tsw" }, "when": { "axis": "y", "top_south_west": true } }, + + + { "apply": { "model": "unicopia:block/straw_bale_bne", "x": 90 }, "when": { "axis": "z", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bnw", "x": 90 }, "when": { "axis": "z", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bse", "x": 90 }, "when": { "axis": "z", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bsw", "x": 90 }, "when": { "axis": "z", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tne", "x": 90 }, "when": { "axis": "z", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tnw", "x": 90 }, "when": { "axis": "z", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tse", "x": 90 }, "when": { "axis": "z", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tsw", "x": 90 }, "when": { "axis": "z", "top_north_west": true } } + ] +} diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 41f63712..90e2a220 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -44,8 +44,11 @@ "item.unicopia.palm_chest_boat": "Palm Boat with Chest", "item.unicopia.spellbook": "Spellbook", + "item.unicopia.spectral_clock": "Spectral Clock", + "emi.category.unicopia.spellbook": "Spellbook", "emi.category.unicopia.cloud_shaping": "Shaping", + "emi.category.unicopia.growing": "Growing", "item.unicopia.alicorn_badge": "Alicorn Emblem", "item.unicopia.unicorn_badge": "Unicorn Emblem", @@ -73,6 +76,7 @@ "item.unicopia.love_bucket": "Love Bucket", "item.unicopia.love_mug": "Mug o' Love", + "item.unicopia.plunder_vine": "Plunder Vine", "item.unicopia.empty_jar": "Glass Jar", "item.unicopia.filled_jar": "%s in a Jar", "item.unicopia.rain_cloud_jar": "Rain in a Jar", @@ -123,6 +127,7 @@ "item.unicopia.green_apple_seeds": "Granny Smith Apple Seeds", "item.unicopia.sweet_apple_seeds": "Sweet Apple Seeds", "item.unicopia.sour_apple_seeds": "Sour Apple Seeds", + "item.unicopia.golden_oak_seeds": "Golden Oak Seeds", "item.unicopia.apple_pie_hoof": "Apple Pie with a Hoofprint", "item.unicopia.apple_pie_slice": "Slice Of Apple Pie", "item.unicopia.candied_apple": "Candied Apple", @@ -246,6 +251,12 @@ "block.unicopia.palm_hanging_sign": "Palm Hanging Sign", "block.unicopia.apple_pie": "Apple Pie", "block.unicopia.weather_vane": "Weather Vane", + "block.unicopia.curing_joke": "Curing Joke", + "block.unicopia.gold_root": "Gold Root", + "block.unicopia.golden_oak_sprout": "Golden Oak Sprout", + "block.unicopia.golden_oak_sapling": "Golden Oak Sapling", + "block.unicopia.golden_oak_leaves": "Golden Oak Leaves", + "block.unicopia.golden_oak_log": "Golden Oak Log", "block.unicopia.mango": "Mango", "block.unicopia.mango_leaves": "Mango Leaves", "block.unicopia.mango_sapling": "Mango Sapling", @@ -307,6 +318,10 @@ "block.unicopia.cloud_bed": "Cloud Bed", "block.unicopia.cloud_chest": "Cloudsdale Chest", "block.unicopia.cloud_chest.double": "Large Cloudsdale Chest", + "block.unicopia.cloud_door": "Cloud Door", + "block.unicopia.crystal_door": "Crystal Door", + "block.unicopia.stable_door": "Stable Door", + "block.unicopia.dark_oak_stable_door": "Wooden Stable Door", "block.unicopia.oats": "Oats", "block.unicopia.oats_stem": "Oats", @@ -322,6 +337,8 @@ "entity.unicopia.sombra.taunt": "That's not going to work on me!", "entity.unicopia.storm_cloud": "Storm Cloud", "entity.unicopia.crystal_shards": "Crystal Shards", + "entity.unicopia.ignominious_vine": "Ignominious Vine", + "entity.unicopia.ignominious_bulb": "Ignominious Bulb", "player.reachDistance": "Reach Distance", "player.miningSpeed": "Mining Speed", @@ -339,47 +356,126 @@ "effect.unicopia.butter_fingers": "Butterfingers", "effect.unicopia.change_race_earth": "Earth Pony Metamorphosis", + "effect.unicopia.change_race_unicorn": "Unicorn Metamorphosis", + "effect.unicopia.change_race_pegasus": "Pegasus Metamorphosis", + "effect.unicopia.change_race_changeling": "Changeling Metamorphosis", + "effect.unicopia.change_race_bat": "Bat Pony Metamorphosis", + "effect.unicopia.change_race_kirin": "Kirin Metamorphosis", + "effect.unicopia.change_race_hippogriff": "Hippogriff Metamorphosis", + + "effect.unicopia.morph_race_earth": "Earth Pony Transformation", + "effect.unicopia.morph_race_unicorn": "Unicorn Transformation", + "effect.unicopia.morph_race_pegasus": "Pegasus Transformation", + "effect.unicopia.morph_race_changeling": "Changeling Transformation", + "effect.unicopia.morph_race_bat": "Bat Pony Transformation", + "effect.unicopia.morph_race_kirin": "Kirin Transformation", + "effect.unicopia.morph_race_hippogriff": "Hippogriff Transformation", + "item.minecraft.potion.effect.unicopia.tribe_swap_earth": "Potion of Earth Pony Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_earth": "Splash Potion of Earth Pony Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_earth": "Lingering Potion of Earth Pony Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_earth": "Arrow of Earth Pony Metamorphosis", - "effect.unicopia.change_race_unicorn": "Unicorn Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_unicorn": "Potion of Unicorn Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_unicorn": "Splash Potion of Unicorn Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_unicorn": "Lingering Potion of Unicorn Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_unicorn": "Arrow of Unicorn Metamorphosis", - "effect.unicopia.change_race_pegasus": "Pegasus Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_pegasus": "Potion of Pegasus Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_pegasus": "Splash Potion of Pegasus Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_pegasus": "Lingering Potion of Pegasus Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_pegasus": "Arrow of Pegasus Metamorphosis", - "effect.unicopia.change_race_changeling": "Changeling Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_changeling": "Potion of Changeling Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_changeling": "Splash Potion of Changeling Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_changeling": "Lingering Potion of Changeling Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_changeling": "Arrow of Changeling Metamorphosis", - "effect.unicopia.change_race_bat": "Bat Pony Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_bat": "Potion of Bat Pony Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_bat": "Splash Potion of Bat Pony Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_bat": "Lingering Potion of Bat Pony Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_bat": "Arrow of Bat Pony Metamorphosis", - "effect.unicopia.change_race_kirin": "Kirin Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_kirin": "Potion of Kirin Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_kirin": "Splash Potion of Kirin Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_kirin": "Lingering Potion of Kirin Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_kirin": "Arrow of Kirin Metamorphosis", - "effect.unicopia.change_race_hippogriff": "Hippogriff Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_hippogriff": "Potion of Hippogriff Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_hippogriff": "Splash Potion of Hippogriff Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_hippogriff": "Lingering Potion of Hippogriff Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_hippogriff": "Arrow of Hippogriff Metamorphosis", + "item.minecraft.potion.effect.unicopia.short_morph_earth": "Potion of Earth Pony Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_earth": "Splash Potion of Earth Pony Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_earth": "Lingering Potion of Earth Pony Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_earth": "Arrow of Earth Pony Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_unicorn": "Potion of Unicorn Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_unicorn": "Splash Potion of Unicorn Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_unicorn": "Lingering Potion of Unicorn Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_unicorn": "Arrow of Unicorn Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_pegasus": "Potion of Pegasus Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_pegasus": "Splash Potion of Pegasus Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_pegasus": "Lingering Potion of Pegasus Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_pegasus": "Arrow of Pegasus Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_changeling": "Potion of Changeling Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_changeling": "Splash Potion of Changeling Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_changeling": "Lingering Potion of Changeling Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_changeling": "Arrow of Changeling Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_bat": "Potion of Bat Pony Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_bat": "Splash Potion of Bat Pony Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_bat": "Lingering Potion of Bat Pony Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_bat": "Arrow of Bat Pony Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_kirin": "Potion of Kirin Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_kirin": "Splash Potion of Kirin Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_kirin": "Lingering Potion of Kirin Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_kirin": "Arrow of Kirin Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_hippogriff": "Potion of Hippogriff Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_hippogriff": "Splash Potion of Hippogriff Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_hippogriff": "Lingering Potion of Hippogriff Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_hippogriff": "Arrow of Hippogriff Short Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_earth": "Potion of Earth Pony Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_earth": "Splash Potion of Earth Pony Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_earth": "Lingering Potion of Earth Pony Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_earth": "Arrow of Earth Pony Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_unicorn": "Potion of Unicorn Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_unicorn": "Splash Potion of Unicorn Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_unicorn": "Lingering Potion of Unicorn Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_unicorn": "Arrow of Unicorn Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_pegasus": "Potion of Pegasus Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_pegasus": "Splash Potion of Pegasus Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_pegasus": "Lingering Potion of Pegasus Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_pegasus": "Arrow of Pegasus Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_changeling": "Potion of Changeling Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_changeling": "Splash Potion of Changeling Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_changeling": "Lingering Potion of Changeling Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_changeling": "Arrow of Changeling Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_bat": "Potion of Bat Pony Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_bat": "Splash Potion of Bat Pony Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_bat": "Lingering Potion of Bat Pony Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_bat": "Arrow of Bat Pony Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_kirin": "Potion of Kirin Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_kirin": "Splash Potion of Kirin Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_kirin": "Lingering Potion of Kirin Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_kirin": "Arrow of Kirin Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_hippogriff": "Potion of Hippogriff Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_hippogriff": "Splash Potion of Hippogriff Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_hippogriff": "Lingering Potion of Hippogriff Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_hippogriff": "Arrow of Hippogriff Long Transformation", + "potion.withChance": "1 in %s chance of %s", "potion.potency.6": "VII", @@ -421,7 +517,7 @@ "spell.unicopia.siphoning.lore": "Channels other creature's life force into the caster", "spell.unicopia.reveal": "Dispell Illusion", "spell.unicopia.reveal.lore": "Negates shapeshifting magic", - "spell.unicopia.light": "Dancing Light", + "spell.unicopia.light": "Dancing Lights", "spell.unicopia.light.lore": "Summons multiple small lights to follow the caster", "spell.unicopia.awkward": "Botched", "spell.unicopia.awkward.lore": "Unstable magics", @@ -699,10 +795,363 @@ "gui.unicopia.spellbook.page.recipes": "Recipes", "gui.unicopia.spellbook.page.recipes.empty": "0 Recipes Unlocked", "gui.unicopia.spellbook.page.mana": "Mana", + "gui.unicopia.spellbook.page.level_requirement": "Level: %s", "gui.unicopia.spellbook.page.requirements.entry.item": "- %1$sx %2$s", "gui.unicopia.spellbook.page.requirements.entry.trait": "- At least %1$sx %2$s trait", "gui.unicopia.spellbook.page.requirements.entry.spell": "- %1$sx %2$s gem", + "gui.unicopia.spellbook.recipe.requires": "Requires:", + "gui.unicopia.spellbook.author1.sign_off": "At the princess' behest", + "gui.unicopia.spellbook.author1.sign_off.b": "At the princess' behest, so dreadfully sorry", + "gui.unicopia.spellbook.author1.name": "- Starswirl the Bearded", + "gui.unicopia.spellbook.author2.name": "- Lord Sombra", + "gui.unicopia.spellbook.author3.name": "- XOXOX Lulu", + "gui.unicopia.spellbook.chapter.artefacts.status.unconfirmed": "Status: Unconfirmed", + "gui.unicopia.spellbook.chapter.artefacts.status.confirmed": "Status: Confirmed", + "gui.unicopia.spellbook.chapter.artefacts.status.lost": "Status: Lost", + "gui.unicopia.spellbook.chapter.introduction.p1.title": "Preface", + "gui.unicopia.spellbook.chapter.introduction.p1.body": "To whomever holds this tome, beware what you seek for you might not like what you find. §kHither yonder equs§r.", + "gui.unicopia.spellbook.chapter.introduction.p2.title": "Ch.1 Magic in Equestria", + "gui.unicopia.spellbook.chapter.introduction.p2.body": "Equestria is filled with magic of all different shapes and forms. Following recent events, however, it's has become plainly obvious that we do not fully understand all that there is about the world of Equestria. That is why the crown has tasked me with researching Magic in all of its forms, so we might utilise it and, I hope, save ourselves from the §kdiscordic assault§r.", + "gui.unicopia.spellbook.chapter.introduction.p3.title": "1st Mare '12", + "gui.unicopia.spellbook.chapter.introduction.p3.1.body": "Unusual Rocks", + "gui.unicopia.spellbook.chapter.introduction.p3.2.body": "These 'Gemstones' as the locals call them are a common material found around the world. Farm-Ponies dig them up all the time and consider it a local delicacy, but I believe these stones are capable of a lot more than they let on.", + "gui.unicopia.spellbook.chapter.introduction.p4.title": "Gemstones", + "gui.unicopia.spellbook.chapter.introduction.p4.1.body": "My research is still incomplete but I may have stumbled upon something. These stones have high magical potentia! More than I've ever seen before!", + "gui.unicopia.spellbook.chapter.introduction.p4.2.body": "§mLuna wants-§r I'm going to keep experimenting. Hooves-crossed, I'll update you tomorrow if I find anything.", + "gui.unicopia.spellbook.chapter.introduction.p5.title": "2nd Mare '12", + "gui.unicopia.spellbook.chapter.introduction.p5.1.body": "It worked! Holy §kCelestia's ass-cheeks'§r it actually worked!", + "gui.unicopia.spellbook.chapter.introduction.p5.2.body": "This is amazing! These can do so much more than I could have ever imagined. Think of the advances I could bring to Equestria. Gem-powered lighting, heating, cooling, I'd no longer have to spend summer sitting on this-", + "gui.unicopia.spellbook.chapter.introduction.p5.3.body": "I'm getting ahead of myself. Let me explain...", + "gui.unicopia.spellbook.chapter.introduction.p6.title": "Spellcrafting", + "gui.unicopia.spellbook.chapter.introduction.p6.1.body": "I drew a guide at the start of this book to help with the placement.", + "gui.unicopia.spellbook.chapter.introduction.p6.2.body": "Put a raw gem-it mustn't have any spells already-in the middle and place materials around it in the slots I marked.", + "gui.unicopia.spellbook.chapter.introduction.p6.3.body": "Each material gives different effects and putting them closer enhances their influence on the gem.", + "gui.unicopia.spellbook.chapter.introduction.p7.title": "3rd Mare '12", + "gui.unicopia.spellbook.chapter.introduction.p7.1.body": "I'm going to start documenting spell combinations as I find them. Some of them are pretty obvious, like gem + fire = fire gem", + "gui.unicopia.spellbook.chapter.introduction.p7.2.body": "But some are less clear. For instance, what traits would an egg add? Much experimenting is needed. Oh, I'm giddy with excitement!", + "gui.unicopia.spellbook.chapter.introduction.p8.title": "Botched Gems", + "gui.unicopia.spellbook.chapter.introduction.p8.1.body": "Not every combination works. What's dissapointing is now I have all these useless stones piling up in my chambers.", + "gui.unicopia.spellbook.chapter.introduction.p8.2.body": "I don't know what to do with them. They're not edible. At least the locals don't think so.", + "gui.unicopia.spellbook.chapter.introduction.p8.3.body": "They do still have the traits I gave them, so maybe I can find a use other than building a rock-fort with little Luna...", + "gui.unicopia.spellbook.chapter.introduction.p9.title": "13th Mare '12", + "gui.unicopia.spellbook.chapter.introduction.p9.1.body": "Sorry for the long delay in updates. I've been hard at work researching different spells and desciding my approach.", + "gui.unicopia.spellbook.chapter.introduction.p9.2.body": "Fire is becomg a very interesting aspect, what with traits for it being readily available.", + + "gui.unicopia.spellbook.chapter.fire.p1.title": "Ch.2 Fire Magic", + "gui.unicopia.spellbook.chapter.fire.p2.title": "9th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p2.1.body": "It took me longer than I anticipated, nearly a month! Hah! But I present to you, dear reader, my findings for the first elementary form of magic: FIRE.", + "gui.unicopia.spellbook.chapter.fire.p2.2.body": "It's a working title, okay?", + "gui.unicopia.spellbook.chapter.fire.scorch.1.body": "Simple and to the point, Scorch does exactly what you'd think. By embuing a gem with the fire trait, one can indute it to glow and become hot to the touch.", + "gui.unicopia.spellbook.chapter.fire.scorch.2.body": "The effect becomes stronger the more fire you load it with, but take care not to overload it, as it may become volatile.", + "gui.unicopia.spellbook.chapter.fire.flame.1.body": "Creates a heating affect up to a radius of 3 hooves from any surfaces it touches.", + "gui.unicopia.spellbook.chapter.fire.flame.2.body": "Useful when one needs a flame in a hurry or to fend off a wild wendigo.", + "gui.unicopia.spellbook.chapter.fire.p5.title": "10th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p5.1.body": "Progress?", + "gui.unicopia.spellbook.chapter.fire.p5.2.body": "I've managed to improve the previous spell somewhat, but there is still something lacking. It's all very orderly. Predicatable.", + "gui.unicopia.spellbook.chapter.fire.p5.3.body": "Luna has suggested adding more fire, but I'm weary to create more scorch marks on the tower. Faust save me if anypony were to find out what I've been doing...", + "gui.unicopia.spellbook.chapter.fire.p6.title": "Fire Magic III", + "gui.unicopia.spellbook.chapter.fire.p6.1.body": "Focusing Magic", + "gui.unicopia.spellbook.chapter.fire.p6.2.body": "Some spells normally take a great amount of focus to cast, and a steady wit to control, However I've found objects embued with the focusing trait work wonderfully as a substitute for when the caster is lacking.", + "gui.unicopia.spellbook.chapter.fire.p7.title": "Fire Magic III-II", + "gui.unicopia.spellbook.chapter.fire.p7.1.body": "Any glass objects you can find, eyes, bottles, whatever incorporates a lense can be used to embue focus on a spell.", + "gui.unicopia.spellbook.chapter.fire.p8.title": "Scrap 2", + "gui.unicopia.spellbook.chapter.fire.p8.1.body": "We went to the market today. Had to get out of that tower, do something, be somewhere. Luna suggested we go in to see what they were selling for the fair so I decided to indulge her.", + "gui.unicopia.spellbook.chapter.fire.p8.2.body": "The townsfolk are still rather skeptical of us, though who's to blame them. Luna was getting along well with the other foals, at least.", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.1.body": "Creates a series of heated projectiles to fling at a target. Upon impact the target will be set alight.", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.2.body": "- Increase focus will allow finer grained control of the projectile's trajectory.", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.3.body": "- With over 50 focus it's almost like they know where the target is (homing?).", + "gui.unicopia.spellbook.chapter.fire.p10.title": "Fire Magic IV", + "gui.unicopia.spellbook.chapter.fire.p10.1.body": "Powerful Magic", + "gui.unicopia.spellbook.chapter.fire.p10.2.body": "Where some spells take focus, others require power. Either to exert a force, or to generate energy in some form.", + "gui.unicopia.spellbook.chapter.fire.p10.3.body": "There are few unicorns that have the inherent strength and power to cast spells of these kinds, but luckily such a trait is not in short supply around us.", + "gui.unicopia.spellbook.chapter.fire.p11.title": "Fire Magic IV-II", + "gui.unicopia.spellbook.chapter.fire.p11.1.body": "Earthly elements, stone, many metals and minerals, that are strong under compression will exhibit the strength trait.", + "gui.unicopia.spellbook.chapter.fire.p11.2.body": "Electrical/Conductive elements that can be used to power things, or that glow can also be used to obtain the power trait.", + "gui.unicopia.spellbook.chapter.fire.p12.title": "11th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p12.1.body": "Got a knock at the door today. Luna was very eager to answer it but I had to send her away as it was a messenger from the crown.", + "gui.unicopia.spellbook.chapter.fire.p12.2.body": "My research has garnered a certain bit of notoriety, it appears. The royals are very eager to see what I have concocted.", + "gui.unicopia.spellbook.chapter.fire.p12.3.body": "They've gotten it in their heads that they can use it against the West.", + "gui.unicopia.spellbook.chapter.fire.p12.4.body": "Gods forbid they succeed. I shudder to think what the council might do if they got their hooves on my work.", + "gui.unicopia.spellbook.chapter.fire.p12.5.body": "Addendum", + "gui.unicopia.spellbook.chapter.fire.p12.6.body": "§mI am told the crown has started giving directions to find other uses. Ways to...", + "gui.unicopia.spellbook.chapter.fire.p13.title": "20th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p13.1.body": "I have word from the crown. They appear satisfied, for now, and have agreed to let my continue my research as I have into the winter.", + "gui.unicopia.spellbook.chapter.fire.p13.2.body": "I am under duress to destroy the last several entries, I'm afraid.", + "gui.unicopia.spellbook.chapter.fire.p14.title": "21st Jum '12", + "gui.unicopia.spellbook.chapter.fire.p14.1.body": "I shall visit §kCommander Hurricane§r tomorrow. Perhaps she may shed light onto my predicament.", + "gui.unicopia.spellbook.chapter.fire.shield.1.body": "Casting shields are one of the first things every unicorn learns in self-defense. It's simple and easy to cast, and is an excellent introduction to incanting.", + "gui.unicopia.spellbook.chapter.fire.shield.2.body": "Its disadvantage is the energy and mental cost, however we can negate both by attaching it to a gem as per follows...", + "gui.unicopia.spellbook.chapter.fire.shield.modifier.1": "+ add power trait to increase effect range", + "gui.unicopia.spellbook.chapter.fire.p16.title": "Protection II", + "gui.unicopia.spellbook.chapter.fire.p16.1.body": "By adding extra traits, I was able to slightly modify the shield to allow or deny certain parties into the effect range.", + "gui.unicopia.spellbook.chapter.fire.p16.2.body": "+ add life trait --> all animals may enter\n+ add blood trait --> all monsters may enter\n+ add ice trait --> all ponies may enter", + "gui.unicopia.spellbook.chapter.fire.p17.title": "Protection III", + "gui.unicopia.spellbook.chapter.fire.p17.1.body": "+ add genorosity trait to attach the spell to a location rather than yourself", + "gui.unicopia.spellbook.chapter.fire.p18.title": "Scrap: 9th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p18.1.body": "Fire magic has proven to a be a little more... unpredictable than anticipated. Every time I feel like I'm making progress it finds a way to set me back.", + "gui.unicopia.spellbook.chapter.fire.p18.2.body": "I can't stop now, though...I'm told the situation in the west is growing dire. They have asked me to pick up the pace and produce something we can use to get the upper hoof against the §kChangeling Storm§r.", + + "gui.unicopia.spellbook.chapter.ice.p1.title": "Ch.3 Ice Magic", + "gui.unicopia.spellbook.chapter.ice.p2.title": "4th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p2.1.body": "This is an interesting one. Rather simple, I admit, but Luna insisted I make something cold to help us deal with this darn heat.", + "gui.unicopia.spellbook.chapter.ice.p2.2.body": "All you need is a gem and something cold. Like a snowball.", + "gui.unicopia.spellbook.chapter.ice.frost.1.body": "Creates a chilling affect up to a radius of 3 hooves from any surfaces it touches.", + "gui.unicopia.spellbook.chapter.ice.frost.2.body": "Will sap energy out of the immediate environment causing a phase change.", + "gui.unicopia.spellbook.chapter.ice.p4.title": "Chilling Breath", + "gui.unicopia.spellbook.chapter.ice.p4.1.body": "Alters the ability of certain objects to distenguish between hot and cold.", + "gui.unicopia.spellbook.chapter.ice.p4.2.body": "This is a very weak spell, but when used with a boat can be exceedingly useful to get out of a sticky stituation.", + "gui.unicopia.spellbook.chapter.ice.p5.title": "5th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p5.1.body": "The village-ponies had a bonfire last night. I could tell by the strong smell of burning wood and the sound of music.", + "gui.unicopia.spellbook.chapter.ice.p5.2.body": "Luna, bless her heart, insisted that we take a break to join them. She had to practically drag me away from my desk to do it.", + "gui.unicopia.spellbook.chapter.ice.p5.3.body": "What can I say? She's a light in my heart.", + "gui.unicopia.spellbook.chapter.ice.p6.title": "Bonfire", + "gui.unicopia.spellbook.chapter.ice.p6.1.body": "We arrived at the bonfire, and of course the first thing they had was a mug of ale in my hoof. I didn't drink it, of course-alcohol is a poison to me. I'd be sick as a mule.", + "gui.unicopia.spellbook.chapter.ice.p6.2.body": "Luna enjoyed it-the bonfire, not the ale!-though. She made immediate friends with one of the town's fillies, Celly or something. They played the whole night.", + "gui.unicopia.spellbook.chapter.ice.p7.title": "Bonfire II", + "gui.unicopia.spellbook.chapter.ice.p7.1.body": "On the way back Luna was telling me of the stories her friend told her. The town has a lot of legends, as to be expected.", + "gui.unicopia.spellbook.chapter.ice.p7.2.body": "One of them was about a scary old warlock who lived in a haunted tower at the edge of town. There's no mystery who that was about.", + "gui.unicopia.spellbook.chapter.ice.p7.3.body": "Ice Spell II", + "gui.unicopia.spellbook.chapter.ice.p7.4.body": "Creates a cooling affect up to a radius of 3 hooves from any surfaces it touches.", + "gui.unicopia.spellbook.chapter.ice.p8.title": "6th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p8.1.body": "There was a strange noise in the village last night. Very strange. I heard a lot ponies shouting and there may have been a fire.", + "gui.unicopia.spellbook.chapter.ice.p8.2.body": "I hope everything is okay.", + "gui.unicopia.spellbook.chapter.ice.light.1.body": "By combining a fire bolt gem with a splash of life and the chilling effect of ice I've created a spell to help with seeing in the night.", + "gui.unicopia.spellbook.chapter.ice.light.2.body": "Dancing Lights will summon a cluster of glowing orbs to illuminate your path.", + "gui.unicopia.spellbook.chapter.ice.light.modifier.1": "* By adding more focus you can extend the duration of the spell", + "gui.unicopia.spellbook.chapter.ice.p10.title": "12th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p10.1.body": "There were more noises last night, this time a lot closer. The town's dimeaner has also changed. A lot of the ponies I see that are normally very cheerful have become sullen.", + "gui.unicopia.spellbook.chapter.ice.p10.2.body": "Something has happened, that much is obvious, though few will tell me what.", + "gui.unicopia.spellbook.chapter.ice.p11.title": "15th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p11.1.body": "Winter is nearly upon us now. I just saw the earliest flakes of snow outside this window as I write.", + "gui.unicopia.spellbook.chapter.ice.p11.2.body": "The locals have begun their winter unwrapping and though the usual grumblings about frozen fields abound, I can tell there is still §ka sense of uneasyness§r.", + "gui.unicopia.spellbook.chapter.ice.p12.title": "17th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p12.1.body": "It's rather surprising how quickly the weather starts to change around here. Everything in Catermoore is so very well controlled, with the spells we use to manage temperature and the pegasi assisting with the weather, we sometimes forget what wild seasons can be like.", + "gui.unicopia.spellbook.chapter.ice.p12.2.body": "But these Earth Ponies don't ave any of those luxuries. They have to deal with the weather as it comes.", + "gui.unicopia.spellbook.chapter.ice.p13.title": "Frozen Lake", + "gui.unicopia.spellbook.chapter.ice.p13.1.body": "Just this morning I was on the way to the stall when I passed the lake at the foot of this tower's hill and saw it was nearly completely frozen over. A few colts had taken to scating across it.", + "gui.unicopia.spellbook.chapter.ice.p13.2.body": "Luna asked if she could join them, but I didn't allow it. I couldn't say why at the time, but I had a bad feeling, like something was going to happen...", + "gui.unicopia.spellbook.chapter.ice.p14.title": "17th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p14.1.body": "Oh gods, of princesses. I. I don't know how to describe this. My hooves are shaking, I can barely breeze. I'm freezing cold and I can't stop thinking about what happened.", + "gui.unicopia.spellbook.chapter.ice.p14.2.body": "I knew there was something wrong. I b---knew it. I didn't know what it was at the time, but I'm extremely glad I didn't let Luna scate on that lake.", + "gui.unicopia.spellbook.chapter.ice.p15.title": "Frozen Lake II", + "gui.unicopia.spellbook.chapter.ice.p15.1.body": "Let me take a step back a bit to describe what happened. Do you remember the frozen lake and those colts that were skating on it?", + "gui.unicopia.spellbook.chapter.ice.p15.2.body": "Well, after that I went on to the market and I was looking at the strawberries in one of the stalls--Didn't get any, sadly they were forgotten in the confusion.", + "gui.unicopia.spellbook.chapter.ice.p16.title": "Frozen Lake III", + "gui.unicopia.spellbook.chapter.ice.p16.1.body": "Whilst I was debating the vendor about the price, I felt Luna tugging on my tunic and she asked me what was going on. I heard galloping and I saw ponies running by us. None of them stopped to explain, but I heard some very gruff words in old ponish.", + "gui.unicopia.spellbook.chapter.ice.p16.2.body": "They were heading in the direction of our tower so I set off after them.", + "gui.unicopia.spellbook.chapter.ice.p16.3.body": "My immediate thoughts were 'was it a fire'? Was my lab in danger?", + "gui.unicopia.spellbook.chapter.ice.p16.4.body": "I tell you what, I wish that were the case. What I actually found was much, much, worse, and even thinking of it makes my blood run cold anew.", + "gui.unicopia.spellbook.chapter.ice.p17.title": "Frozen Lake IV", + "gui.unicopia.spellbook.chapter.ice.p17.1.body": "When we were getting near the lake from earlier, I saw a large crowd growing along its banks. Ladders and emergency equipment were out and scattered on the shoreline and a loud uproar had erupted about what to do.", + "gui.unicopia.spellbook.chapter.ice.p17.2.body": "Getting closer, though, I realised what had happened soon enough--and I made a beeline for for the water-- The ice was broken and the lake was freezing cold.", + "gui.unicopia.spellbook.chapter.ice.p17.3.body": "I jumped in anyway, pulling my saddlebags open with my magic, and grabbed the last of the gems i had with my and cast the unfinished spell it had inside.", + "gui.unicopia.spellbook.chapter.ice.p17.4.body": "The waters receded away from me as I galloped down the slop and across the drying lake bed and dove to catch the colts that had fallen in.", + "gui.unicopia.spellbook.chapter.ice.p18.title": "Frozen Lake V", + "gui.unicopia.spellbook.chapter.ice.p18.1.body": "They were freezing and wet, even as the spell's effects worked to pull the water away from their coats, we carred them up to the shore and got them covered in blankets with hot drinks in their hooves.", + "gui.unicopia.spellbook.chapter.ice.p18.2.body": "The townponies insisted on giving me a blanket of my own, even though I hadn't - couldn't have- gotten wet.", + "gui.unicopia.spellbook.chapter.ice.p18.3.body": "It was only much later, when the shock began to wear of and I was feeling my head start to pound that I remembered to cancel the spell.", + "gui.unicopia.spellbook.chapter.ice.p18.4.body": "Thank the princesses we got there in time.", + "gui.unicopia.spellbook.chapter.ice.p19.title": "18th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p19.1.body": "No sign of the colts this morning, I assume they won't be coming near this lake for a long while. The water had frozen again in the night and looked peaceful.", + "gui.unicopia.spellbook.chapter.ice.p19.2.body": "The unseasy feeling I had yesterday was gone today so I was able to relax on its banks with Luna. She didn't want to swim in this lake any more, and I don't blame her. I wouldn't either.", + "gui.unicopia.spellbook.chapter.ice.p20.title": "Sandcastle", + "gui.unicopia.spellbook.chapter.ice.p20.1.body": "Luna started a sand castle, and whilst she was busy with that I decided to sketch out the details of my new spell.", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.1.body": "By combining the abilities of a shield spell with that of the frost gem, the results are... Admittedly strange.", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.2.body": "I'm calling this water repulsion because it does just that: It pushes water away from the caster.", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.modifier.1": "* By adding more focus you can extend the duration of the spell", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.modifier.2": "* Add the generosity trait to tie this spell to a location rather than a user", + + "gui.unicopia.spellbook.chapter.air.p1.title": "Ch.4 Air Magic", + "gui.unicopia.spellbook.chapter.air.p2.title": "1st Hoof '12", + "gui.unicopia.spellbook.chapter.air.p1.1.body": "A new month, a new chapter. Little Luna was getting bored of sitting in the tower all day (and who's to blame her? We've been on this assignment for over two months at this point).", + "gui.unicopia.spellbook.chapter.air.p1.2.body": "So as a little treat, we've decided to go on a little trip to the Grand Marepid Falls to visit my friend, Commander Hurricane.", + "gui.unicopia.spellbook.chapter.air.p1.3.body": "The Commander has also very graciously allowed me access to her library to continue my studies. I'm excited to see what combining unicorn and pegasus magics might bring.", + "gui.unicopia.spellbook.chapter.air.p3.title": "2nd Hoof '12", + "gui.unicopia.spellbook.chapter.air.p3.1.body": "Apologies for the, um, unusual entry in the appendices for today. It appears some little gremlin managed to obscond with my journal.", + "gui.unicopia.spellbook.chapter.air.p4.title": "Air Magic I", + "gui.unicopia.spellbook.chapter.air.p4.1.body": "Pegasus Magic", + "gui.unicopia.spellbook.chapter.air.p4.2.body": "Air magic is to pegasi like fire is to unicorns. They're both equally hard to control but where fire is primarily focused around force, destruction, or protection, air is all about flexibility and free motion.", + "gui.unicopia.spellbook.chapter.air.p4.3.body": "Command Hurricane has very kindly given me some tips on how to identify this trait in everyday objects.", + "gui.unicopia.spellbook.chapter.air.p4.4.body": "Anything relating to flight, or that originated from creatures that fly, or that comes from up above can be considered a source of the air trait.", + "gui.unicopia.spellbook.chapter.air.p4.5.body": "Eg. Feathers.", + "gui.unicopia.spellbook.chapter.air.catapult.1.body": "This is a straightforward application of a unicorn's telekineses. The catapult gem allows a caster to grab any block or creature and fling them into the air.", + "gui.unicopia.spellbook.chapter.air.catapult.2.body": "Use it again on something already thrown to push it away from you.", + "gui.unicopia.spellbook.chapter.air.catapult.modifier.1": "* One can add apply more force by adding the strength trait", + "gui.unicopia.spellbook.chapter.air.bubble.1.body": "A defensive and utility spell. Bubble will trap the target in a giant soap bubble, rendering them defensless.", + "gui.unicopia.spellbook.chapter.air.bubble.2.body": "Use it again will pop the bubble.", + "gui.unicopia.spellbook.chapter.air.p7.title": "8th Hoof '12", + "gui.unicopia.spellbook.chapter.air.p7.1.body": "I thought I would take a short moment to write down an entry to record my findings whilst Luna and The Commander are out.", + "gui.unicopia.spellbook.chapter.air.p7.2.body": "Pegasi magic really is a fascinating beast. It's not like our magic, which is more of a study, with rigid rules and practices.", + "gui.unicopia.spellbook.chapter.air.p8.title": "Pegasi", + "gui.unicopia.spellbook.chapter.air.p8.1.body": "Pegasus magic is more about feeling. It's an art form. They don't think about what they want to do, it just happens, but it all still follows the same principle.", + "gui.unicopia.spellbook.chapter.air.p9.title": "Pegasi II", + "gui.unicopia.spellbook.chapter.air.p9.1.body": "Take for example their cloud homes. There are no spells I can read that would let me do this, but if you feel, not with your hooves or your horn, but properly, with your mind, your heart, you will find magic buzzing all throughout.", + "gui.unicopia.spellbook.chapter.air.p9.2.body": "Clouds are teeming with the air and water trait, but also others, like power, strength, life, earth. All traits of the material the clouds are mimicking.", + "gui.unicopia.spellbook.chapter.air.p10.title": "Pegasi III", + "gui.unicopia.spellbook.chapter.air.p10.1.body": "I wish I could study this further, but I'm afraid to interfere in this magic I don't yet fully understand.", + "gui.unicopia.spellbook.chapter.air.p10.2.body": "Commander Hurricane may never forgive me if I destroy her home the first time I'm here.", + "gui.unicopia.spellbook.chapter.air.feather_fall.1.body": "Expanding on the defensive capabilities of the protection gem, I've attempted to extend its advantages to party members.", + "gui.unicopia.spellbook.chapter.air.feather_fall.2.body": "This one is unusual because of its complexity, but in theory it should allow one to slow their own and friends' descent.", + "gui.unicopia.spellbook.chapter.air.p12.title": "10th Hoof '12", + "gui.unicopia.spellbook.chapter.air.p12.1.body": "Went to dinner with Commander Huricane and Luna. We got to talking about architecture and Hurricane mentioned the Taz Marehall.", + "gui.unicopia.spellbook.chapter.air.p12.2.body": "Luna thought it was a rather funny name of a castle. I had to remind her that not all cultures are the same.", + "gui.unicopia.spellbook.chapter.air.p13.title": "21st Hoof '12", + "gui.unicopia.spellbook.chapter.air.p13.1.body": "I'm writing this on the eve of my return to §kTrotholm§r. Though my time in Cloudopolis has been elightening, I look forward to a return to the familiar surroundings and a proper rest in my own solid bed.", + "gui.unicopia.spellbook.chapter.air.p13.2.body": "I cannot say the same for Luna, though. She is currently sitting on my bed beside me pouting over every little thing she sees me put into my saddlebag.", + "gui.unicopia.spellbook.chapter.air.p14.title": "Returning Home", + "gui.unicopia.spellbook.chapter.air.p14.1.body": "She keeps insisting that we stay a little longer §mto hang out with that pegasus colt I saw her with the other day, no doubt", + "gui.unicopia.spellbook.chapter.air.p15.title": "22nd Hoof '12", + "gui.unicopia.spellbook.chapter.air.p15.1.body": "As we're flying above the mountain tops, I can't help but feel in awe the beauty that §kMother Faust§r has given us.", + "gui.unicopia.spellbook.chapter.air.p15.2.body": "I had to spend the whole time keeping Luna from falling out, and of course answering questions about all the different kinds of clouds. Thankfully, the balloon operator was there to help with the subtleties.", + "gui.unicopia.spellbook.chapter.air.p15.3.body": "Being out here, above all of our troubles, really makes me forget the rest of the world for a moment. It takes me back to a simpler time, before the--", + "gui.unicopia.spellbook.chapter.air.p15.4.body": "Of course the flashes of explosions to the west has to ruin it all. There appears to be a storm on the horizon. I certainly hope it not to be a bad omen...", + + "gui.unicopia.spellbook.chapter.dark_magic.p1.title": "Ch.5 The Arcane", + "gui.unicopia.spellbook.chapter.dark_magic.p2.title": "30th Hoof '12", + "gui.unicopia.spellbook.chapter.dark_magic.p2.1.body": "A new communication from the crown today. The situation seems to be worsening in the west and though they've been satisfied with my work until now, they are putting pressure on me to produce something more destructive.", + "gui.unicopia.spellbook.chapter.dark_magic.p2.2.body": "I tried to tell the messenger that I couldn't-", + "gui.unicopia.spellbook.chapter.dark_magic.p3.title": "3rd Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p3.1.body": "Let it be known that it was never my intention that anypony use my work for nefarious purposes. I am a researcher, above and beyond all else. My intentions are pure, and my wants are nothing more than this world to be at peace.", + "gui.unicopia.spellbook.chapter.dark_magic.p4.title": "4th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p4.1.body": "No review of magic is ever complete without a glimpse into the other side.", + "gui.unicopia.spellbook.chapter.dark_magic.p4.2.body": "Dark magic, or as I'm going to refer to it as The Arcane are tip-toeing the line between the normal light magic we normally know and the more nefarious side of reality.", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.1.body": "If someone were to ask you 'what is the opposite of a repulsion spell, what would you say? An attraction spell, of course!", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.2.body": "By twisting the purpose of the protection spell, I've been able to reverse its function to create a spell that pulls objects and entities closer to the caster.", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.modifier.1": "+ 10x knowledge to narrow the effect's range to items", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.modifier.2": "+ add focus trait to increase duration\n+ add power trait to increase range", + "gui.unicopia.spellbook.chapter.dark_magic.p6.title": "8th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p6.1.body": "Additional Notes for the Attraction Spell", + "gui.unicopia.spellbook.chapter.dark_magic.p6.2.body": "I caught Luna playing with my spellcrafting grid today, even though I expressly forbid her from entering my study when I'm not there.", + "gui.unicopia.spellbook.chapter.dark_magic.p6.3.body": "Apparently it was over some dispute with Celly, I don't really remember, but it culminated in Luna sneaking into the study whilst I was out to get some bread.", + "gui.unicopia.spellbook.chapter.dark_magic.p7.title": "Arcane Attraction II", + "gui.unicopia.spellbook.chapter.dark_magic.p7.1.body": "This isn't really about that, though. She's been scolded and sent back to her room, however as I was cleaning up the mess she'd made I noticed something in the piles of gems.", + "gui.unicopia.spellbook.chapter.dark_magic.p7.2.body": "It's hard to describe, really. This is still distincly an attraction gem, but it's different.", + "gui.unicopia.spellbook.chapter.dark_magic.p7.3.body": "It has traits I hadn't considered before, and the way it behaves... ", + "gui.unicopia.spellbook.chapter.dark_magic.p8.title": "Arcane Attraction II Cont.", + "gui.unicopia.spellbook.chapter.dark_magic.p8.1.body": "Well I'll leave that up to tomorrow. I'm still tired from everything that's happened this week.", + "gui.unicopia.spellbook.chapter.dark_magic.p8.2.body": ">0 generosity --> ??", + "gui.unicopia.spellbook.chapter.dark_magic.p8.3.body": ">20 order trait --> ???", + "gui.unicopia.spellbook.chapter.dark_magic.p9.title": "20th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p9.1.body": "As per their agreement, the council have sent certain...supplimental materials to aid in the new direction my research is taking. I was a little shocked at first.", + "gui.unicopia.spellbook.chapter.dark_magic.p9.2.body": "This... thing... Whatever it is. Was, rather.", + "gui.unicopia.spellbook.chapter.dark_magic.p9.3.body": "Is this really what we're fighting in the west?", + "gui.unicopia.spellbook.chapter.dark_magic.p10.title": "21st Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p10.1.body": "I've put the... thing. In the basement. Locked the door.", + "gui.unicopia.spellbook.chapter.dark_magic.p10.2.body": "I just couldn't stand looking at it any longer. It's vaguely pony-shaped, but also...", + "gui.unicopia.spellbook.chapter.dark_magic.p10.3.body": "I couldn't well let Luna see it. I've sent her out to spend the next few nights with Celly whilst I sort out what to do with this.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.title": "25th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p11.1.body": "I'm sorry for the long delays. Things have been... busy.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.2.body": "I've learned a lot about these creatures. Attached are some illustrations, done best I could so I wouldn't have to look at the thing directly.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.3.body": "Its body is black and vaguely §kinsect-like§r with ponish proportions. It has no fur.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.4.body": "The magic they use is unusual. Not unusual, like what I've been studying. It's unnatural. Wild.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.5.body": "There is definitely something I might be able to harness here, but I shudder... Should I?", + "gui.unicopia.spellbook.chapter.dark_magic.p11.6.body": "I fear that this may be a line that shouldn't be crossed.", + "gui.unicopia.spellbook.chapter.dark_magic.transformation.1.body": "I've begun by simply harnessing their ability. It's unfocused and hard to control. I can rarely predict what is going to happen, but this gem has very clear transmodrification properties.", + "gui.unicopia.spellbook.chapter.dark_magic.transformation.2.body": "Throwing this at any creature has the chance to transform it into any other creature.", + "gui.unicopia.spellbook.chapter.dark_magic.reveal.1.body": "Dispell Illusion is the first line of defense against transformation/illusion spells.", + "gui.unicopia.spellbook.chapter.dark_magic.reveal.2.body": "When cast it will force any nearby disguised changelings in its range to reveal their true form.", + "gui.unicopia.spellbook.chapter.dark_magic.reveal.modifier.1": "* Increase range by adding the power trait", + "gui.unicopia.spellbook.chapter.dark_magic.p14.title": "27th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p14.1.body": "Sleep the last few nights has become... elusive.", + "gui.unicopia.spellbook.chapter.dark_magic.p14.2.body": "I don't know how to describe it, really. Luna appears unaffected, but every night after the sun goes down I find myself lying awake at night unable to sleep. It doesn't help that that sounds in the village have resumed.", + "gui.unicopia.spellbook.chapter.dark_magic.p15.title": "Lost Sleep", + "gui.unicopia.spellbook.chapter.dark_magic.p15.1.body": "Last night, especially, I found myself pacing in the observatory. The air became chill, more than is normal for this time of year, and beyond anything that raging fire in corner of the room could combat.", + "gui.unicopia.spellbook.chapter.dark_magic.p15.2.body": "The room where I keep the--my patient opposite the wall behind me.", + "gui.unicopia.spellbook.chapter.dark_magic.p16.title": "Lost Sleep Cont.", + "gui.unicopia.spellbook.chapter.dark_magic.p16.1.body": "I've long since taken to keeping that door locked because every so often I could swear I heard something moving in there...", + "gui.unicopia.spellbook.chapter.dark_magic.p16.2.body": "Gods, am I going crazy?", + "gui.unicopia.spellbook.chapter.dark_magic.p17.title": "29th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p17.1.body": "Bits, there it is again!", + "gui.unicopia.spellbook.chapter.dark_magic.p17.2.body": "I thought last night was a fluke, but I just heard it again--I'm shaking. My hooves, I can barely hold this book as I struggle to pen these words.", + "gui.unicopia.spellbook.chapter.dark_magic.p17.3.body": "There's something--I heard something. Like a chittering--", + "gui.unicopia.spellbook.chapter.dark_magic.p18.title": "1st Croptober '12", + "gui.unicopia.spellbook.chapter.dark_magic.p18.1.body": "I made a call into town. One of the local blacksmiths have agreed to install a new lock, one of the heavy kind that not even earth ponies can bust.", + "gui.unicopia.spellbook.chapter.dark_magic.p18.2.body": "I fear it may not be enough, though, it--whatever it is, clearly has magic. I may be forced to research a magical solution to my insomnia.", + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.1.body": "This spell will create a magical shroud that can be used to protect from other spellcasters.", + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.2.body": "No one else will be able to use magic within its radius.", + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.modifier.1": "* Increase range by adding the power trait", + "gui.unicopia.spellbook.chapter.dark_magic.displacement.1.body": "By casting this spell, a unicorn is able to swap their location with any other creature.", + "gui.unicopia.spellbook.chapter.dark_magic.p21.title": "3rd Croptober '12", + "gui.unicopia.spellbook.chapter.dark_magic.p21.1.body": "The locks have been installed, and with the addition of some extra wardings, I'm feeling a little more at ease.", + "gui.unicopia.spellbook.chapter.dark_magic.p21.2.body": "The motions at night have all but stopped, though I feel like I can almost hear it at times...", + "gui.unicopia.spellbook.chapter.dark_magic.p22.title": "4th Cropt-", + "gui.unicopia.spellbook.chapter.dark_magic.mimic.1.body": "I've been able to tap into some of this strange creature's abilities. There's still a lot to figure out here, but for now I've merely distilled its essense into a gem.", + "gui.unicopia.spellbook.chapter.dark_magic.mimic.modifier.1": "* Add the focus trait to increase the effect's duration", + "gui.unicopia.spellbook.chapter.dark_magic.p24.1.body": "There's been a wave of darkness that has come over the town. Nothing's been the same since that gods-forsaken creature arrived.", + "gui.unicopia.spellbook.chapter.dark_magic.p24.2.body": "Ponies in town have begun remarking on lack of sleep, and it's showing. Just today I saw a poor stallion walking around in a daze. Bags under his eyes, barely any colour in his cheeks.", + "gui.unicopia.spellbook.chapter.dark_magic.p25.1.body": "He looked almost like a zombie the way he went through the motions, not really paying any attention even after he nearly ran into me.", + "gui.unicopia.spellbook.chapter.dark_magic.p26.title": "Scrap", + "gui.unicopia.spellbook.chapter.dark_magic.p26.1.body": "§kIt's not enough. Never enough. Crawling. I feel crawling. Oh gods the crawling won't stop.", + "gui.unicopia.spellbook.chapter.dark_magic.p27.1.body": "The insomnia. I can't take it. I lie in my bed every night waiting to go to sleep.", + "gui.unicopia.spellbook.chapter.dark_magic.p27.2.body": "I thought I could stop it, keep it at bay, but I still hear it. That creature. Cold, unfeeling.", + "gui.unicopia.spellbook.chapter.dark_magic.p27.3.body": "I feel myself being drained any time I'm around it. Is it... feeding on me?", + "gui.unicopia.spellbook.chapter.dark_magic.p27.4.body": "No, it couldn't be.", + "gui.unicopia.spellbook.chapter.dark_magic.p28.1.body": "I found this incantation under some old notes whilst clearing out the lab. It's... simplistic and hard to manage, but it gets the job done.", + "gui.unicopia.spellbook.chapter.dark_magic.dispel_evil.modifier.1": "* Add the power trait to increase the effect's range", + + "gui.unicopia.spellbook.chapter.otherworldly.p1.title": "Ch.6 The Beyond", + "gui.unicopia.spellbook.chapter.otherworldly.p2.title": "2nd Croptober '12", + "gui.unicopia.spellbook.chapter.otherworldly.p2.1.body": "This chapter serves as an exploration of the worlds beyond our own and a delving into what most unicorns would normally shy away from.", + "gui.unicopia.spellbook.chapter.otherworldly.p2.2.body": "In this section can be found the most powerful of the powerful spells, but also the most danger. Read further at your own peril, as this is not for the light of mind.", + "gui.unicopia.spellbook.chapter.otherworldly.siphoning.1.body": "A simple spell that siphons life force out of a living entity and uses it to revitalise the caster.", + "gui.unicopia.spellbook.chapter.otherworldly.necromancy.1.body": "This area effect spell taps into the great beyond to summon life to serve its master.", + "gui.unicopia.spellbook.chapter.otherworldly.necromancy.2.body": "This spell is not very useful when used on its own, but combined with other traits may become a powerful tool against the §kChangeling Swarms§r.", + "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.1.body": "Dipping below the fabric of reality, this spell taps into the deep arcane powers of the beyond to punch a hole in reality.", + "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.2.body": "The resulting hole is a hungry mass that consumes all who approach. It grants massive energy, but feed it at your peril.", + "gui.unicopia.spellbook.chapter.otherworldly.portal.1.body": "Combining the effects of the displacement spell created by my predecessor and the dark vortex gem, one is able to tame its chaotic nature.", + "gui.unicopia.spellbook.chapter.otherworldly.portal.2.body": "The arcane rift spell allows the caster to link two locations together to create a bridge across the ether. Anything that enters one end will appear at the other maintaining its velocity.", + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.1.body": "Continuing my predecessor's research into the abilities of the Changeling Spawn, I have enhanced his mimic spell by adding a chaotic twist.", + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.2.body": "Mind Swap extends the effects of mimic to cover two individuals, in effect swapping their bodies for a limited time.", + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.3.body": "* Add the focus trait to increase the effect's duration", + + "gui.unicopia.spellbook.chapter.artefacts.p1.title": "Ch.7 Artefacts", + "gui.unicopia.spellbook.chapter.artefacts.p2.title": "2nd Mare '12", + "gui.unicopia.spellbook.chapter.artefacts.p2.1.body": "What follows is a compendium of research into certain objects of interest identified through my research.", + "gui.unicopia.spellbook.chapter.artefacts.p2.2.body": "Not all of these have gotten anywhere, as they have been proven to either be pure legend, or perhaps a hoax, so I cannot put stock into their stories.", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.1.body": "Crafted by a group of unicorns long ago, its origin and current location is unknown.", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.2.body": "What few accounts exist claim it was a powerful tool of protection and support, as it would funnel life force from enemies towards the caster and their allies.", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.title": "5th Mare '12", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.3.body": "Other accounts say that this artefact only functions when mounted on a specific pedestal of diamond blocks, like a beacon.", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.title": "Torn Page", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.1.body": "§kaaa§rential c§ka§rr§kaaa§rnial purpose§kaaa§r?", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.2.body": "§kAasa sasa fwefsd q43rgfd wqklmsdfl as, klasn.§r", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.3.body": "Building Materials:", + "gui.unicopia.spellbook.chapter.artefacts.crystal_podium.title": "Crystal Podium", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.2.body": "It's, um a scroll that you write somepony's name on it and you hold it in one hoof and something in the other hoof and, like, um it goes whooosh and the item is sent to that pony.", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.title": "2nd Hoof '12", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.3.body": "P.S. Uncle Starswirly is a dunderhead.", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.1.body": "Used in the past by spellcasters to communicate over long distances, this band has long since lost its old use.", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.2.body": "Unicorns who sign and hand out this band can use it to share certain magic effects with their friends.", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.title": "13th Mare '12", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.3.body": "Anyone wearing a bangle you have signed will be able to benefit from the positive effects of your spells, or will be allowed through protection and shield spells.", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.4.body": "Mana costs are also shared equally between all nearby members.", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.1.body": "Commander Hurricane informed me of this, though I've found little texts to back up his claims.", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.2.body": "The Pegasus Amulet is claimed to grant the wearer temporary flight, like a pegasus.", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.title": "21st Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.3.body": "It was intended as an aide for early unicorn ambassadors to Cloud Heights, but was lost after negotiations broke down.", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.1.body": "A precursor to magic staffs, the meadwobrook's staff is an upright support structure commonly used by warlocks during long incantation sessions.", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.2.body": "It features a twisting and mottled shape with a dense and sturdy core capable of supporting the weight of an average-sized, adult male.", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.title": "22nd Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.3.body": "Due to its dense structure and flamability, this object also serves a secondary purpose as an offensive weapon and fuel source should the situation demand.", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.4.body": "To use correctly in combat, one must begin by gripping the staff by the narrow end in both hands, followed by a swift swing from above one's head whilst yelling 'Fus Roh DAH!'", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.1.body": "Magical aides for non-magical users. Magic staffs work in a similar way to a unicorns horn in that they can be used to channel and harness the innate magic stored within gems.", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.title": "22nd Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.2.body": "Not all spells work in the same way, but for those that do, a good staff is an essential tool for any beginner magi.", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.3.body": "The simplest way to create these is to put a gem on the end of a stick. Yes, very revolutionary, I know.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.1.body": "A powerful artifact once thought to be the source of King Grogar's power.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.2.body": "In skilled hooves, the bell may be used to transfer magical energy from one being to another.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.title": "22nd Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.3.body": "It's theorised this artifact was forged from the claws of an Ursa Major during the era of Discord's reign, possibly as a weapon against the tyrant.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.4.body": "By its nature, the bell a corrupting influence inherent to it that will destroy the minds of anyone who dares weird its power.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.2.title": "Grogar's Bell II", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.5.body": "Legend says that after its first bearer, King Grogar, was driven to madness, the bell was stowed far away.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.6.body": "beyond most ponies' grasp, guarded inside an ancient city by an unbeatable beast.", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.1.body": "Like the crystal heart, little is known of this artefact and thus nothing, not even its existance can be confirmed.", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.title": "23nd Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.2.body": "The alicorn amulet is a powerful force of dark magic created created by an unknown mage as their attempt to create the perfect being.", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.3.body": "It combines the traits of all races into one powerful form, but it hard to control and addictive in nature. Anyone who uses it quickly becomes reliant on it and few attempt to remove it survive the ordeal.", + "gui.unicopia.action.spells_cleared": "Removed all spells", "gui.unicopia.action.no_spells_cleared": "You have no active spells", @@ -985,6 +1434,10 @@ "death.attack.unicopia.rock.self": "%1$s was pummeled", "death.attack.unicopia.rock.item": "%1$s was pummelled by %2$s using %3$s", "death.attack.unicopia.rock.player": "%1$s was pummelled by %2$s", + "death.attack.unicopia.horseshoe": "%1$s went ding", + "death.attack.unicopia.horseshoe.self": "%1$s dinged himself", + "death.attack.unicopia.horseshoe.item": "%1$s was dinged by %2$s using %3$s", + "death.attack.unicopia.horseshoe.player": "%1$s was dinged by %2$s", "death.fell.accident.ladder.pegasus": "%1$s forgot they could fly and fell off a ladder", "death.fell.accident.vines.pegasus": "%1$s forgot they could fly and fell off some vines", @@ -1150,7 +1603,9 @@ "advancements.unicopia.earth_route.title": "Path of the Pony", "advancements.unicopia.earth_route.description": "Join the Apple Clan", "advancements.unicopia.sticks_and_stones.title": "Sticks and Stones", - "advancements.unicopia.sticks_and_stones.description": "Kill an mob by throwing rocks at it", + "advancements.unicopia.sticks_and_stones.description": "Kill a mob by throwing rocks at it", + "advancements.unicopia.dead_ringer.title": "Dead Ringer", + "advancements.unicopia.dead_ringer.description": "Kill a mob with a horseshoe", "advancements.unicopia.born_on_a_rock_farm.title": "Born on a Rock Farm", "advancements.unicopia.born_on_a_rock_farm.description": "Successfully farm your first rock", "advancements.unicopia.thats_unusual.title": "That's Unusual", diff --git a/src/main/resources/assets/unicopia/lang/ru_ru.json b/src/main/resources/assets/unicopia/lang/ru_ru.json index d7ce9dc2..eb31d7f4 100644 --- a/src/main/resources/assets/unicopia/lang/ru_ru.json +++ b/src/main/resources/assets/unicopia/lang/ru_ru.json @@ -123,6 +123,7 @@ "item.unicopia.green_apple_seeds": "Семена яблони Гренни Смит", "item.unicopia.sweet_apple_seeds": "Семена яблони Яблочной Аллеи", "item.unicopia.sour_apple_seeds": "Семена кислой яблони", + "item.unicopia.golden_oak_seeds": "Семена золотого дуба", "item.unicopia.apple_pie_hoof": "Яблочный пирог с отпечатком копыта", "item.unicopia.apple_pie_slice": "Кусочек яблочного пирога", "item.unicopia.candied_apple": "Засахаренное яблоко", @@ -246,6 +247,12 @@ "block.unicopia.palm_hanging_sign": "Пальмовая подвесная табличка", "block.unicopia.apple_pie": "Яблочный пирог", "block.unicopia.weather_vane": "Флюгер", + "block.unicopia.curing_joke": "Лечащая шутка", + "block.unicopia.gold_root": "Золотой корень", + "block.unicopia.golden_oak_sprout": "Росток золотого дуба", + "block.unicopia.golden_oak_sapling": "Саженец золотого дуба", + "block.unicopia.golden_oak_leaves": "Листья золотого дуба", + "block.unicopia.golden_oak_log": "Бревно золотого дуба", "block.unicopia.mango": "Манго", "block.unicopia.mango_leaves": "Листья мангового дерева", "block.unicopia.mango_sapling": "Саженец мангового дерева", @@ -298,9 +305,19 @@ "block.unicopia.dense_cloud_slab": "Плита из плотного облака", "block.unicopia.dense_cloud_stairs": "Ступеньки из плотного облака", "block.unicopia.compacted_dense_cloud": "Плотное облако", + "block.unicopia.etched_cloud": "Выгравированное облако", + "block.unicopia.etched_cloud_slab": "Плита выгравированного облака", + "block.unicopia.etched_cloud_stairs": "Ступеньки из выгравированного облака", + "block.unicopia.compacted_etched_cloud": "Выгравированное облако", "block.unicopia.cloud_pillar": "Колонна из облака", "block.unicopia.cloth_bed": "Кровать из шикарной ткани", "block.unicopia.cloud_bed": "Кровать из облака", + "block.unicopia.cloud_chest": "Клаудсдейльский сундук", + "block.unicopia.cloud_chest.double": "Большой клаудсдейльский сундук", + "block.unicopia.cloud_door": "Облачная дверь", + "block.unicopia.crystal_door": "Кристальная дверь", + "block.unicopia.stable_door": "Дверь конюшни", + "block.unicopia.dark_oak_stable_door": "Деревянная дверь конюшни", "block.unicopia.oats": "Овёс", "block.unicopia.oats_stem": "Овёс", @@ -316,6 +333,8 @@ "entity.unicopia.sombra.taunt": "На меня это не подействует!", "entity.unicopia.storm_cloud": "Штормовое облако", "entity.unicopia.crystal_shards": "Кристальные осколки", + "entity.unicopia.ignominious_vine": "Позорная лоза", + "entity.unicopia.ignominious_bulb": "Позорная лампочка", "player.reachDistance": "Расстояние до цели", "player.miningSpeed": "Скорость добычи", @@ -374,6 +393,7 @@ "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_hippogriff": "Туманное зелье метаморфоз гиппогрифа", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_hippogriff": "Стрела метаморфоз гиппогрифа", + "potion.withChance": "Шанс 1 к %s получить %s", "potion.potency.6": "VII", "spell.unicopia.frost": "Заморозка", @@ -468,11 +488,57 @@ "trait.unicopia.poison.name": "Яда", "trait.unicopia.poison.description": "Смертоносный дротик убивает зверя", - "toxicity.safe.name": "Безопасный", - "toxicity.mild.name": "Слаботоксичный", - "toxicity.fair.name": "Достаточно токсичный", - "toxicity.severe.name": "Токсичный", - "toxicity.lethal.name": "Смертельный", + "unicopia.diet.information": "Информация о диете:", + "unicopia.diet.side_effects": "Побочные эффекты:", + "unicopia.diet.not_edible": "Предмет не съедобен", + "unicopia.diet.base_multiplier": "Базовый множитель: %s%%", + "unicopia.diet.hunger.detailed": "Голода восстановлено: %s из %s (%s%%)", + "unicopia.diet.saturation.detailed": "Насыщения получено: %s (%s%%)", + "unicopia.diet.hunger": "Коэффициент голода: %s%%", + "unicopia.diet.saturation": "Коэффициент насыщения: %s%%", + + "tag.unicopia.food_types.rotten_meat": "Гниющее мясо", + "tag.unicopia.food_types.raw_meat": "Свежее мясо", + "tag.unicopia.food_types.cooked_meat": "Готовое мясо", + "tag.unicopia.food_types.raw_fish": "Свежая рыба", + "tag.unicopia.food_types.cooked_fish": "Готовая рыба", + "tag.unicopia.food_types.raw_insect": "Жуки и насекомые", + "tag.unicopia.food_types.cooked_insect": "Приготовленные жуки и насекомые", + "tag.unicopia.food_types.love": "Любовь", + "tag.unicopia.food_types.rocks": "Камни", + "tag.unicopia.food_types.pinecone": "Орехи и семена", + "tag.unicopia.food_types.bat_ponys_delight": "Лакомства бэтпони", + "tag.unicopia.food_types.cooked_sea_vegitables": "Готовая рыбная еда", + "tag.unicopia.food_types.raw_sea_vegitables": "Свежая рыбная еда", + "tag.unicopia.food_types.shells": "Морские ракушки", + "tag.unicopia.food_types.shelly": "Морские ракушки", + "tag.unicopia.food_types.candy": "Конфеты", + "tag.unicopia.food_types.desserts": "Десерты", + "tag.unicopia.food_types.fruits_and_vegetables": "Фрукты и овощи", + "tag.unicopia.food_types.drinks": "Напитки", + + "tag.unicopia.food_types.forage_edible_filling": "Крупная растительная масса", + "tag.unicopia.food_types.forage_edible": "Растительная масса", + "tag.unicopia.food_types.forage_nauseating": "Тошнотворное", + "tag.unicopia.food_types.forage_prickly": "Колючее", + "tag.unicopia.food_types.forage_risky": "Небезопасное", + "tag.unicopia.food_types.forage_strengthening": "Повышающее силу", + "tag.unicopia.food_types.forage_severely_prickly": "Очень колючее", + "tag.unicopia.food_types.forage_severely_nauseating": "Отвратительное", + "tag.unicopia.food_types.forage_radioactive": "Светящееся", + "tag.unicopia.food_types.forage_dangerous": "Опасное", + "tag.unicopia.food_types.forage_blinding": "Токсичное", + + "toxicity.safe.name": "Безопасное", + "toxicity.mild.name": "Слаботоксичное", + "toxicity.fair.name": "Достаточно токсичное", + "toxicity.severe.name": "Токсичное", + "toxicity.lethal.name": "Смертельное", + + "affliction.unicopia.empty": "Нет эффекта", + "affliction.unicopia.healing": "Восполняет %s%% здоровья", + "affliction.unicopia.cure_love_sickness": "Лечит от любовной болезни", + "affliction.unicopia.lose_hunger": "Теряет %s%% сытости", "ability.unicopia.shoot": "Стрелять магией", "ability.unicopia.shoot.with_spell": "Стрелять \"%s\"", @@ -779,7 +845,7 @@ "enchantment.unicopia.heavy": "Тяжесть", "enchantment.unicopia.herds": "Стада", "enchantment.unicopia.want_it_need_it": "Хочу это, нужно это", - "enchantment.unicopia.poisoned_joke": "Отравленная шутка", + "enchantment.unicopia.poisoned_joke": "Ядовитая шутка", "enchantment.unicopia.stressed": "Напряжённость", "enchantment.unicopia.heart_bound": "Связанный сердцем", "enchantment.unicopia.consumption": "Потребление", @@ -925,13 +991,17 @@ "death.attack.unicopia.kick.player": "%2$s очень сильно пнул %1$s", "death.attack.unicopia.steamroller": "%1$s был расплющен", "death.attack.unicopia.steamroller.self": "%1$s был расплющен", - "death.attack.unicopia.steamroller.player": "%2$s рассправился с %1$s", - "death.attack.unicopia.stalagmite.pegasus": "%1$s пытался устроиться на сталагмите", + "death.attack.unicopia.steamroller.player": "%2$s расплющил %1$s", + "death.attack.unicopia.stalagmite.pegasus": "%1$s попытался устроиться на сталагмите", "death.attack.unicopia.stalagmite.pegasus.player": "%1$s влетел в сталагмит пока боролся %2$s", - "death.attack.unicopia.rock": "%1$s был избит", - "death.attack.unicopia.rock.self": "%1$s был избит", - "death.attack.unicopia.rock.item": "%1$s был избит %2$s используя %3$s", - "death.attack.unicopia.rock.player": "%1$s был избит %2$s", + "death.attack.unicopia.rock": "%1$s был обстрелян камнями", + "death.attack.unicopia.rock.self": "%1$s обстрелял себя камнями", + "death.attack.unicopia.rock.item": "%1$s был обстрелян камнями %2$s используя %3$s", + "death.attack.unicopia.rock.player": "%1$s был обстрелян камнями %2$s", + "death.attack.unicopia.horseshoe": "В %1$s прилетела подкова", + "death.attack.unicopia.horseshoe.self": "%1$s зарядил подковой в себя", + "death.attack.unicopia.horseshoe.item": "%2$s зарядил подковой в %1$s используя %3$s", + "death.attack.unicopia.horseshoe.player": "%2$s зарядил подковой в %1$s", "death.fell.accident.ladder.pegasus": "%1$s забыл, что умеет летать, и упал с лестницы", "death.fell.accident.vines.pegasus": "%1$s забыл, что умеет летать, и упал с лозы", @@ -1098,6 +1168,8 @@ "advancements.unicopia.earth_route.description": "Вступить в клан Яблока", "advancements.unicopia.sticks_and_stones.title": "Палки и камни", "advancements.unicopia.sticks_and_stones.description": "Убейте моба, бросая в него камни", + "advancements.unicopia.dead_ringer.title": "Звон смерти", + "advancements.unicopia.dead_ringer.description": "Убейте моба подковой", "advancements.unicopia.born_on_a_rock_farm.title": "Рождённый на Ферме Камней", "advancements.unicopia.born_on_a_rock_farm.description": "Успешно вырастить свой первый камень", "advancements.unicopia.thats_unusual.title": "Это необычно", @@ -1127,6 +1199,8 @@ "advancements.unicopia.tempted.description": "Наденьте амулет аликорна", "advancements.unicopia.hello_darkness_my_old_friend.title": "Здравствуй, Тьма...", "advancements.unicopia.hello_darkness_my_old_friend.description": "Углубитесь в силы тёмной стороны", + "advancements.unicopia.a_falling_wizard.title": "Падший волшебник", + "advancements.unicopia.a_falling_wizard.description": "Единорог безуспешно попытался наложить заклинание полёта", "advancements.unicopia.split_the_sea.title": "Ты кто? Моисей?", "advancements.unicopia.split_the_sea.description": "Используйте магию, чтобы переместить более 100 блоков воды за один раз", diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index c9a7106b..9eb2718d 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -123,6 +123,7 @@ "item.unicopia.green_apple_seeds": "史密斯婆婆苹果的种子", "item.unicopia.sweet_apple_seeds": "甜苹果的种子", "item.unicopia.sour_apple_seeds": "酸苹果的种子", + "item.unicopia.golden_oak_seeds": "金橡树种子", "item.unicopia.apple_pie_hoof": "蹄印苹果派", "item.unicopia.apple_pie_slice": "一片苹果派", "item.unicopia.candied_apple": "苹果蜜饯", @@ -151,9 +152,9 @@ "item.unicopia.clam_shell": "蛤蜊壳", "item.unicopia.scallop_shell": "扇贝壳", "item.unicopia.turret_shell": "锥螺壳", - "item.unicopia.shelly": "壳儿", + "item.unicopia.shelly": "贝利", - "item.unicopia.horse_shoe.accuracy": "精准度%d%%", + "item.unicopia.horse_shoe.accuracy": "精准度%:d%%", "item.unicopia.horse_shoe.speed": "速度:%d", "item.unicopia.iron_horse_shoe": "铁蹄铁", "item.unicopia.golden_horse_shoe": "金蹄铁", @@ -189,30 +190,30 @@ "item.unicopia.music_disc_funk.desc": "funk, just funk", "item.unicopia.cloud_lump": "云团", - "item.unicopia.light_gray_bed_sheets": "淡灰色床单", - "item.unicopia.gray_bed_sheets": "灰色床单", - "item.unicopia.black_bed_sheets": "黑色床单", - "item.unicopia.brown_bed_sheets": "棕色床单", - "item.unicopia.red_bed_sheets": "红色床单", - "item.unicopia.orange_bed_sheets": "橙色床单", - "item.unicopia.yellow_bed_sheets": "黄色床单", - "item.unicopia.lime_bed_sheets": "黄绿色床单", - "item.unicopia.green_bed_sheets": "绿色床单", - "item.unicopia.cyan_bed_sheets": "青色床单", - "item.unicopia.light_blue_bed_sheets": "淡蓝色床单", - "item.unicopia.blue_bed_sheets": "蓝色床单", - "item.unicopia.purple_bed_sheets": "紫色床单", - "item.unicopia.magenta_bed_sheets": "品红色床单", - "item.unicopia.pink_bed_sheets": "粉色床单", - "item.unicopia.apple_bed_sheets": "苹果纹饰床单", - "item.unicopia.barred_bed_sheets": "条纹床单", - "item.unicopia.checkered_bed_sheets": "棋盘格床单", - "item.unicopia.kelp_bed_sheets": "海藻纹饰床单", - "item.unicopia.rainbow_bed_sheets": "彩虹纹饰床单", - "item.unicopia.rainbow_bpw_bed_sheets": "BPW 彩虹纹饰床单", - "item.unicopia.rainbow_bpy_bed_sheets": "BPY 彩虹纹饰床单", - "item.unicopia.rainbow_pbg_bed_sheets": "PGB 彩虹纹饰床单", - "item.unicopia.rainbow_pwr_bed_sheets": "PWR 彩虹纹饰床单", + "item.unicopia.light_gray_bed_sheets": "淡灰色被单", + "item.unicopia.gray_bed_sheets": "灰色被单", + "item.unicopia.black_bed_sheets": "黑色被单", + "item.unicopia.brown_bed_sheets": "棕色被单", + "item.unicopia.red_bed_sheets": "红色被单", + "item.unicopia.orange_bed_sheets": "橙色被单", + "item.unicopia.yellow_bed_sheets": "黄色被单", + "item.unicopia.lime_bed_sheets": "黄绿色被单", + "item.unicopia.green_bed_sheets": "绿色被单", + "item.unicopia.cyan_bed_sheets": "青色被单", + "item.unicopia.light_blue_bed_sheets": "淡蓝色被单", + "item.unicopia.blue_bed_sheets": "蓝色被单", + "item.unicopia.purple_bed_sheets": "紫色被单", + "item.unicopia.magenta_bed_sheets": "品红色被单", + "item.unicopia.pink_bed_sheets": "粉色被单", + "item.unicopia.apple_bed_sheets": "苹果纹饰被单", + "item.unicopia.barred_bed_sheets": "条纹被单", + "item.unicopia.checkered_bed_sheets": "棋盘格被单", + "item.unicopia.kelp_bed_sheets": "海藻纹饰被单", + "item.unicopia.rainbow_bed_sheets": "彩虹纹饰被单", + "item.unicopia.rainbow_bpw_bed_sheets": "跨性 彩虹纹饰被单", + "item.unicopia.rainbow_bpy_bed_sheets": "泛性 彩虹纹饰被单", + "item.unicopia.rainbow_pbg_bed_sheets": "无性 彩虹纹饰被单", + "item.unicopia.rainbow_pwr_bed_sheets": "女性 彩虹纹饰被单", "block.unicopia.rocks": "一些石块", "block.unicopia.plunder_vine": "掠夺之藤", @@ -246,6 +247,12 @@ "block.unicopia.palm_hanging_sign": "悬挂式棕榈木告示牌", "block.unicopia.apple_pie": "苹果派", "block.unicopia.weather_vane": "风向标", + "block.unicopia.curing_joke": "疗玩笑", + "block.unicopia.gold_root": "黄金根", + "block.unicopia.golden_oak_sprout": "金橡树嫩芽", + "block.unicopia.golden_oak_sapling": "金橡树树苗", + "block.unicopia.golden_oak_leaves": "金橡树树叶", + "block.unicopia.golden_oak_log": "金橡树原木", "block.unicopia.mango": "芒果", "block.unicopia.mango_leaves": "芒果树叶", "block.unicopia.mango_sapling": "芒果树苗", @@ -307,6 +314,10 @@ "block.unicopia.cloud_bed": "云床", "block.unicopia.cloud_chest": "云中箱", "block.unicopia.cloud_chest.double": "大云中箱", + "block.unicopia.cloud_door": "云中门", + "block.unicopia.crystal_door": "水晶门", + "block.unicopia.stable_door": "马厩门", + "block.unicopia.dark_oak_stable_door": "木马厩门", "block.unicopia.oats": "燕麦", "block.unicopia.oats_stem": "燕麦", @@ -322,6 +333,8 @@ "entity.unicopia.sombra.taunt": "用在我身上不好使!", "entity.unicopia.storm_cloud": "暴风云", "entity.unicopia.crystal_shards": "水晶尖刺", + "entity.unicopia.ignominious_vine": "无耻藤", + "entity.unicopia.ignominious_bulb": "无耻球茎", "player.reachDistance": "可触距离", "player.miningSpeed": "挖掘速度", @@ -339,47 +352,126 @@ "effect.unicopia.butter_fingers": "手滑", "effect.unicopia.change_race_earth": "转生陆马", + "effect.unicopia.change_race_unicorn": "转生独角兽", + "effect.unicopia.change_race_pegasus": "转生天马", + "effect.unicopia.change_race_changeling": "转生幻形灵", + "effect.unicopia.change_race_bat": "转生夜骐", + "effect.unicopia.change_race_kirin": "转生麒麟", + "effect.unicopia.change_race_hippogriff": "转生骏鹰", + + "effect.unicopia.morph_race_earth": "变成陆马", + "effect.unicopia.morph_race_unicopia": "变成独角兽", + "effect.unicopia.morph_race_pegasus": "变成天马", + "effect.unicopia.morph_race_changeling": "变成幻形灵", + "effect.unicopia.morph_race_bat": "变成夜骐", + "effect.unicopia.morph_race_kirin": "变成麒麟", + "effect.unicopia.morph_race_hippogriff": "变成骏鹰", + "item.minecraft.potion.effect.unicopia.tribe_swap_earth": "转生陆马药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_earth": "喷溅型转生陆马药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_earth": "滞留型转生陆马药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_earth": "转生陆马之箭", - "effect.unicopia.change_race_unicorn": "转生独角兽", "item.minecraft.potion.effect.unicopia.tribe_swap_unicorn": "转生独角兽药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_unicorn": "喷溅型转生独角兽药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_unicorn": "滞留型转生独角兽药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_unicorn": "转生独角兽之箭", - "effect.unicopia.change_race_pegasus": "转生天马", "item.minecraft.potion.effect.unicopia.tribe_swap_pegasus": "转生天马药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_pegasus": "喷溅型转生天马药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_pegasus": "滞留型转生天马药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_pegasus": "转生天马之箭", - "effect.unicopia.change_race_changeling": "转生幻形灵", "item.minecraft.potion.effect.unicopia.tribe_swap_changeling": "转生幻形灵药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_changeling": "喷溅型转生幻形灵药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_changeling": "滞留型转生幻形灵药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_changeling": "转生幻形灵之箭", - "effect.unicopia.change_race_bat": "转生夜骐", "item.minecraft.potion.effect.unicopia.tribe_swap_bat": "转生夜骐药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_bat": "喷溅型转生夜骐药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_bat": "滞留型转生夜骐药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_bat": "转生夜骐之箭", - "effect.unicopia.change_race_kirin": "转生麒麟", "item.minecraft.potion.effect.unicopia.tribe_swap_kirin": "转生麒麟药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_kirin": "喷溅型转生麒麟药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_kirin": "滞留型转生麒麟药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_kirin": "转生麒麟之箭", - "effect.unicopia.change_race_hippogriff": "转生骏鹰", "item.minecraft.potion.effect.unicopia.tribe_swap_hippogriff": "转生骏鹰药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_hippogriff": "喷溅型转生骏鹰药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_hippogriff": "滞留型转生骏鹰药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_hippogriff": "转生骏鹰之箭", - + + "item.minecraft.potion.effect.unicopia.short_morph_earth": "短效变成陆马药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_earth": "喷溅型短效变成陆马药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_earth": "滞留型短效变成陆马药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_earth": "短效变成陆马之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_unicorn": "短效变成独角兽药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_unicorn": "喷溅型短效变成独角兽药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_unicorn": "滞留型短效变成独角兽药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_unicorn": "短效变成独角兽之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_pegasus": "短效变成天马药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_pegasus": "喷溅型短效变成天马药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_pegasus": "滞留型短效变成天马药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_pegasus": "短效变成天马之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_changeling": "短效变成幻形灵药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_changeling": "喷溅型短效变成幻形灵药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_changeling": "滞留型短效变成幻形灵药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_changeling": "短效变成幻形灵之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_bat": "短效变成夜骐药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_bat": "喷溅型短效变成夜骐药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_bat": "滞留型短效变成夜骐药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_bat": "短效变成夜骐之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_kirin": "短效变成麒麟药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_kirin": "喷溅型短效变成麒麟药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_kirin": "滞留型短效变成麒麟药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_kirin": "短效变成麒麟之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_hippogriff": "短效变成骏鹰药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_hippogriff": "喷溅型短效变成骏鹰药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_hippogriff": "滞留型短效变成骏鹰药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_hippogriff": "短效变成骏鹰之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_earth": "长效变成陆马药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_earth": "喷溅型长效变成陆马药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_earth": "滞留型长效变成陆马药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_earth": "长效变成陆马之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_unicorn": "长效变成独角兽药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_unicorn": "喷溅型长效变成独角兽药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_unicorn": "滞留型长效变成独角兽药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_unicorn": "长效变成独角兽之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_pegasus": "长效变成天马药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_pegasus": "喷溅型长效变成天马药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_pegasus": "滞留型长效变成天马药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_pegasus": "长效变成天马之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_changeling": "长效变成幻形灵药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_changeling": "喷溅型长效变成幻形灵药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_changeling": "滞留型长效变成幻形灵药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_changeling": "长效变成幻形灵之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_bat": "长效变成夜骐药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_bat": "喷溅型长效变成夜骐药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_bat": "滞留型长效变成夜骐药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_bat": "长效变成夜骐之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_kirin": "长效变成麒麟药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_kirin": "喷溅型长效变成麒麟药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_kirin": "滞留型长效变成麒麟药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_kirin": "长效变成麒麟之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_hippogriff": "长效变成骏鹰药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_hippogriff": "喷溅型长效变成骏鹰药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_hippogriff": "滞留型长效变成骏鹰药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_hippogriff": "长效变成骏鹰之箭", + "potion.withChance": "%s 分之 1 的概率为 %s", "potion.potency.6": "VII", @@ -469,15 +561,15 @@ "trait.unicopia.generosity.name": "慷慨", "trait.unicopia.generosity.description": "施展利他魔咒。\n可以用于增加魔咒的范围和威力,但对施法者不利。\n\n与协律元素和善良元素互相协同。\n含有这种要素的魔咒适合用来支援朋友。", "trait.unicopia.rot.name": "腐坏", - "trait.unicopia.rot.description": "死亡与毁灭踏足了这个世界。一切都会消亡,一切都将消亡。过去如此,将来亦是。", + "trait.unicopia.rot.description": "死亡与毁灭踏足了这个世界。一切都会消亡,一切都将消亡。这既是历史,也是未来。", "trait.unicopia.life.name": "生命", "trait.unicopia.life.description": "一切活着的存在都蕴含的力量。", "trait.unicopia.poison.name": "毒性", "trait.unicopia.poison.description": "一枚绝命毒镖能轻松放倒一头巨兽。", - "unicopia.diet.information": "饮食信息:", + "unicopia.diet.information": "食物情况:", "unicopia.diet.side_effects": "副作用:", - "unicopia.diet.not_edible": "物品不可食用", + "unicopia.diet.not_edible": "吃不了", "unicopia.diet.base_multiplier": "基础倍率: %s%%", "unicopia.diet.hunger.detailed": "获取饥饿值: %s of %s (%s%%)", "unicopia.diet.saturation.detailed": "获取饱和度: %s (%s%%)", @@ -617,7 +709,7 @@ "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.earth": " - 拥有强大的击,踢和踏力,可用于防御或向四周出击", "gui.unicopia.tribe_selection.confirm.goods.5.unicopia.earth": " - 唯一能和苦力怕拥抱而不会被炸死的种族", "gui.unicopia.tribe_selection.confirm.goods.6.unicopia.earth": " - 可以吃石头", - "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.earth": " - 可爱软乎的小马耳朵", + "gui.unicopia.tribe_selection.confirm.goods.7.unicopia.earth": " - 可爱软乎的小马耳朵", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.unicorn": " - 瞬移和魔法咒语", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.unicorn": " - 研究和制作增强自身能力的魔法神器", @@ -628,7 +720,7 @@ "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.pegasus": " - 使用储存的魔力在短时间内冲刺或蓄力做出彩虹音爆", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.pegasus": " - 移动速度更快,摔落伤害更低", "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.pegasus": " - 可以直接和云与云类物品互动", - "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.pegasus": " - 可以吃蔬菜和某些鱼", + "gui.unicopia.tribe_selection.confirm.goods.5.unicopia.pegasus": " - 可以吃蔬菜和某些鱼", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.bat": " - 能够飞行,能通过训练提高耐力", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.bat": " - 夜间看得更清楚", @@ -638,7 +730,7 @@ "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.changeling": " - 能够在原地飞行和悬停", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.changeling": " - 几乎能变形成任何生物或任何物体", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.changeling": " - 通过爬墙可越过任何任何物体", - "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.changeling": " - 是肉食性的。可以吃任何不会让它们生病的东西", + "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.changeling": " - 是肉食性的。可以吃任何不会让它们生病的东西", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.kirin": " - 免疫一切火焰伤害", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.kirin": " - 受到伤害后可以发怒", @@ -985,6 +1077,10 @@ "death.attack.unicopia.rock.self": "%1$s 被击中了", "death.attack.unicopia.rock.item": "%1$s 被 %2$s 用 %3$s 击中了", "death.attack.unicopia.rock.player": "%1$s 被 %2$s 击中了", + "death.attack.unicopia.horseshoe": "%1$s 被咣了", + "death.attack.unicopia.horseshoe.self": "%1$s 咣了自己", + "death.attack.unicopia.horseshoe.item": "%1$s 被 %2$s 用 %3$s 咣了", + "death.attack.unicopia.horseshoe.player": "%1$s 被 %2$s 咣了", "death.fell.accident.ladder.pegasus": "%1$s 从梯子上掉下来时忘了自己会飞", "death.fell.accident.vines.pegasus": "%1$s 从藤蔓上掉下来时忘了自己会飞", @@ -1102,7 +1198,7 @@ "advancements.unicopia.eat_pinecone.title": "豁出去了", "advancements.unicopia.eat_pinecone.description": "吃下一颗松果", "advancements.unicopia.imported_oats.title": "昂贵的味道", - "advancements.unicopia.imported_oats.description": "用魔法邮寄高档进口燕麦", + "advancements.unicopia.imported_oats.description": "寄出或收到高档进口燕麦", "advancements.unicopia.experimental.title": "你不能带走它", "advancements.unicopia.experimental.description": "给一件工具附上经验提取的魔力", @@ -1151,6 +1247,8 @@ "advancements.unicopia.earth_route.description": "入伙苹果帮", "advancements.unicopia.sticks_and_stones.title": "岩与攻击", "advancements.unicopia.sticks_and_stones.description": "扔出石块砸死一位生灵", + "advancements.unicopia.dead_ringer.title": "死亡回旋", + "advancements.unicopia.dead_ringer.description": "用蹄铁砸死一位生灵", "advancements.unicopia.born_on_a_rock_farm.title": "种石得石", "advancements.unicopia.born_on_a_rock_farm.description": "播种石籽", "advancements.unicopia.thats_unusual.title": "不对劲", diff --git a/src/main/resources/assets/unicopia/models/block/curing_joke.json b/src/main/resources/assets/unicopia/models/block/curing_joke.json new file mode 100644 index 00000000..d99c056b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/curing_joke.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cross", + "textures": { + "cross": "unicopia:block/curing_joke" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/gold_root_stage0.json b/src/main/resources/assets/unicopia/models/block/gold_root_stage0.json new file mode 100644 index 00000000..8f0b22cd --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/gold_root_stage0.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/crop", + "textures": { + "crop": "unicopia:block/gold_root_stage0" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/gold_root_stage1.json b/src/main/resources/assets/unicopia/models/block/gold_root_stage1.json new file mode 100644 index 00000000..5315c2b8 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/gold_root_stage1.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/crop", + "textures": { + "crop": "unicopia:block/gold_root_stage1" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/gold_root_stage2.json b/src/main/resources/assets/unicopia/models/block/gold_root_stage2.json new file mode 100644 index 00000000..cf262db5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/gold_root_stage2.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/crop", + "textures": { + "crop": "unicopia:block/gold_root_stage2" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/gold_root_stage3.json b/src/main/resources/assets/unicopia/models/block/gold_root_stage3.json new file mode 100644 index 00000000..790fd135 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/gold_root_stage3.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/crop", + "textures": { + "crop": "unicopia:block/gold_root_stage3" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/golden_apple.json b/src/main/resources/assets/unicopia/models/block/golden_apple.json new file mode 100644 index 00000000..a2a567a9 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_apple.json @@ -0,0 +1,6 @@ +{ + "parent": "unicopia:block/fruit", + "textures": { + "cross": "minecraft:item/golden_apple" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/golden_oak_leaves.json b/src/main/resources/assets/unicopia/models/block/golden_oak_leaves.json new file mode 100644 index 00000000..0c8f2bd9 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_oak_leaves.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/leaves", + "textures": { + "all": "unicopia:block/golden_oak_leaves" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/golden_oak_log.json b/src/main/resources/assets/unicopia/models/block/golden_oak_log.json new file mode 100644 index 00000000..d4909cb0 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_oak_log.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/cube_column", + "textures": { + "end": "unicopia:block/golden_oak_log_top", + "side": "unicopia:block/golden_oak_log" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/golden_oak_log_horizontal.json b/src/main/resources/assets/unicopia/models/block/golden_oak_log_horizontal.json new file mode 100644 index 00000000..8dc80c7b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_oak_log_horizontal.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/cube_column_horizontal", + "textures": { + "end": "unicopia:block/golden_oak_log_top", + "side": "unicopia:block/golden_oak_log" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/golden_oak_sapling.json b/src/main/resources/assets/unicopia/models/block/golden_oak_sapling.json new file mode 100644 index 00000000..c4c2ba33 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_oak_sapling.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cross", + "textures": { + "cross": "unicopia:item/golden_oak_sapling" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_bne.json b/src/main/resources/assets/unicopia/models/block/hay_bale_bne.json new file mode 100644 index 00000000..7453b2e9 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_bne.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "bottom_north_east", + "from": [8, 0, 0], + "to": [16, 8, 8], + "faces": { + "north": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "south": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_bnw.json b/src/main/resources/assets/unicopia/models/block/hay_bale_bnw.json new file mode 100644 index 00000000..6cde59a7 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_bnw.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "bottom_north_west", + "from": [0, 0, 0], + "to": [8, 8, 8], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_bse.json b/src/main/resources/assets/unicopia/models/block/hay_bale_bse.json new file mode 100644 index 00000000..76ec139b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_bse.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "bottom_south_east", + "from": [8, 0, 8], + "to": [16, 8, 16], + "faces": { + "north": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "east": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "south": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "west": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "up": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "down": {"uv": [8, 0, 16, 8], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_bsw.json b/src/main/resources/assets/unicopia/models/block/hay_bale_bsw.json new file mode 100644 index 00000000..ed4ade6d --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_bsw.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "bottom_south_west", + "from": [0, 0, 8], + "to": [8, 8, 16], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "east": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "west": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_tne.json b/src/main/resources/assets/unicopia/models/block/hay_bale_tne.json new file mode 100644 index 00000000..d23896b4 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_tne.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "top_north_east", + "from": [8, 8, 0], + "to": [16, 16, 8], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "south": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_tnw.json b/src/main/resources/assets/unicopia/models/block/hay_bale_tnw.json new file mode 100644 index 00000000..b465617c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_tnw.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "top_north_west", + "from": [0, 8, 0], + "to": [8, 16, 8], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_tse.json b/src/main/resources/assets/unicopia/models/block/hay_bale_tse.json new file mode 100644 index 00000000..64d34fa6 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_tse.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "top_south_east", + "from": [8, 8, 8], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "east": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "south": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "up": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "down": {"uv": [8, 0, 16, 8], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_tsw.json b/src/main/resources/assets/unicopia/models/block/hay_bale_tsw.json new file mode 100644 index 00000000..01f205b0 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_tsw.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "top_south_west", + "from": [0, 8, 8], + "to": [8, 16, 16], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "east": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_bne.json b/src/main/resources/assets/unicopia/models/block/rice_bale_bne.json new file mode 100644 index 00000000..3c280742 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_bne.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bne", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_bnw.json b/src/main/resources/assets/unicopia/models/block/rice_bale_bnw.json new file mode 100644 index 00000000..9b3d6ce0 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_bnw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bnw", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_bse.json b/src/main/resources/assets/unicopia/models/block/rice_bale_bse.json new file mode 100644 index 00000000..6fd0b774 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_bse.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bse", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_bsw.json b/src/main/resources/assets/unicopia/models/block/rice_bale_bsw.json new file mode 100644 index 00000000..c2ec227a --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_bsw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bsw", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_tne.json b/src/main/resources/assets/unicopia/models/block/rice_bale_tne.json new file mode 100644 index 00000000..81eb613c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_tne.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tne", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_tnw.json b/src/main/resources/assets/unicopia/models/block/rice_bale_tnw.json new file mode 100644 index 00000000..c4bef47d --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_tnw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tnw", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_tse.json b/src/main/resources/assets/unicopia/models/block/rice_bale_tse.json new file mode 100644 index 00000000..f58ce7d1 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_tse.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tse", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_tsw.json b/src/main/resources/assets/unicopia/models/block/rice_bale_tsw.json new file mode 100644 index 00000000..9608f77e --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_tsw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tsw", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_bne.json b/src/main/resources/assets/unicopia/models/block/straw_bale_bne.json new file mode 100644 index 00000000..fef62f35 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_bne.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bne", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_bnw.json b/src/main/resources/assets/unicopia/models/block/straw_bale_bnw.json new file mode 100644 index 00000000..a5b3162b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_bnw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bnw", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_bse.json b/src/main/resources/assets/unicopia/models/block/straw_bale_bse.json new file mode 100644 index 00000000..95d23675 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_bse.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bse", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_bsw.json b/src/main/resources/assets/unicopia/models/block/straw_bale_bsw.json new file mode 100644 index 00000000..3a5ef025 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_bsw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bsw", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_tne.json b/src/main/resources/assets/unicopia/models/block/straw_bale_tne.json new file mode 100644 index 00000000..f8cc37a5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_tne.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tne", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_tnw.json b/src/main/resources/assets/unicopia/models/block/straw_bale_tnw.json new file mode 100644 index 00000000..1b06dba9 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_tnw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tnw", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_tse.json b/src/main/resources/assets/unicopia/models/block/straw_bale_tse.json new file mode 100644 index 00000000..44d1f681 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_tse.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tse", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_tsw.json b/src/main/resources/assets/unicopia/models/block/straw_bale_tsw.json new file mode 100644 index 00000000..0c74b850 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_tsw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tsw", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/curing_joke.json b/src/main/resources/assets/unicopia/models/item/curing_joke.json new file mode 100644 index 00000000..65c5811a --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/curing_joke.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/curing_joke" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/golden_oak_leaves.json b/src/main/resources/assets/unicopia/models/item/golden_oak_leaves.json new file mode 100644 index 00000000..14afba2f --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/golden_oak_leaves.json @@ -0,0 +1,3 @@ +{ + "parent": "unicopia:block/golden_oak_leaves" +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/golden_oak_log.json b/src/main/resources/assets/unicopia/models/item/golden_oak_log.json new file mode 100644 index 00000000..699482c5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/golden_oak_log.json @@ -0,0 +1,3 @@ +{ + "parent": "unicopia:block/golden_oak_log" +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/golden_oak_sapling.json b/src/main/resources/assets/unicopia/models/item/golden_oak_sapling.json new file mode 100644 index 00000000..cc4be552 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/golden_oak_sapling.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/golden_oak_sapling" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/golden_oak_seeds.json b/src/main/resources/assets/unicopia/models/item/golden_oak_seeds.json new file mode 100644 index 00000000..16cb1fc6 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/golden_oak_seeds.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/golden_oak_seeds" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/plunder_vine.json b/src/main/resources/assets/unicopia/models/item/plunder_vine.json new file mode 100644 index 00000000..32b343c6 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/plunder_vine.json @@ -0,0 +1,3 @@ +{ + "parent": "unicopia:block/plunder_vine_bud" +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock.json b/src/main/resources/assets/unicopia/models/item/spectral_clock.json new file mode 100644 index 00000000..7328e905 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock.json @@ -0,0 +1,49 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_00" + }, + "overrides": [ + { "predicate": { "unicopia:zap_cycle": 0.000}, "model": "unicopia:item/spectral_clock" }, + { "predicate": { "unicopia:zap_cycle": 0.025}, "model": "unicopia:item/spectral_clock_05" }, + { "predicate": { "unicopia:zap_cycle": 0.050}, "model": "unicopia:item/spectral_clock_10" }, + { "predicate": { "unicopia:zap_cycle": 0.075}, "model": "unicopia:item/spectral_clock_15" }, + { "predicate": { "unicopia:zap_cycle": 0.100}, "model": "unicopia:item/spectral_clock_20" }, + { "predicate": { "unicopia:zap_cycle": 0.125}, "model": "unicopia:item/spectral_clock_25" }, + { "predicate": { "unicopia:zap_cycle": 0.150}, "model": "unicopia:item/spectral_clock_30" }, + { "predicate": { "unicopia:zap_cycle": 0.175}, "model": "unicopia:item/spectral_clock_35" }, + { "predicate": { "unicopia:zap_cycle": 0.200}, "model": "unicopia:item/spectral_clock_greening_00" }, + { "predicate": { "unicopia:zap_cycle": 0.225}, "model": "unicopia:item/spectral_clock_greening_05" }, + { "predicate": { "unicopia:zap_cycle": 0.250}, "model": "unicopia:item/spectral_clock_greening_10" }, + { "predicate": { "unicopia:zap_cycle": 0.275}, "model": "unicopia:item/spectral_clock_greening_15" }, + { "predicate": { "unicopia:zap_cycle": 0.300}, "model": "unicopia:item/spectral_clock_greening_20" }, + { "predicate": { "unicopia:zap_cycle": 0.325}, "model": "unicopia:item/spectral_clock_greening_25" }, + { "predicate": { "unicopia:zap_cycle": 0.350}, "model": "unicopia:item/spectral_clock_greening_30" }, + { "predicate": { "unicopia:zap_cycle": 0.375}, "model": "unicopia:item/spectral_clock_greening_35" }, + { "predicate": { "unicopia:zap_cycle": 0.400}, "model": "unicopia:item/spectral_clock_flowering_00" }, + { "predicate": { "unicopia:zap_cycle": 0.425}, "model": "unicopia:item/spectral_clock_flowering_05" }, + { "predicate": { "unicopia:zap_cycle": 0.450}, "model": "unicopia:item/spectral_clock_flowering_10" }, + { "predicate": { "unicopia:zap_cycle": 0.475}, "model": "unicopia:item/spectral_clock_flowering_15" }, + { "predicate": { "unicopia:zap_cycle": 0.500}, "model": "unicopia:item/spectral_clock_flowering_20" }, + { "predicate": { "unicopia:zap_cycle": 0.525}, "model": "unicopia:item/spectral_clock_flowering_25" }, + { "predicate": { "unicopia:zap_cycle": 0.550}, "model": "unicopia:item/spectral_clock_flowering_30" }, + { "predicate": { "unicopia:zap_cycle": 0.575}, "model": "unicopia:item/spectral_clock_flowering_35" }, + { "predicate": { "unicopia:zap_cycle": 0.600}, "model": "unicopia:item/spectral_clock_fruiting_00" }, + { "predicate": { "unicopia:zap_cycle": 0.625}, "model": "unicopia:item/spectral_clock_fruiting_05" }, + { "predicate": { "unicopia:zap_cycle": 0.650}, "model": "unicopia:item/spectral_clock_fruiting_10" }, + { "predicate": { "unicopia:zap_cycle": 0.675}, "model": "unicopia:item/spectral_clock_fruiting_15" }, + { "predicate": { "unicopia:zap_cycle": 0.700}, "model": "unicopia:item/spectral_clock_fruiting_20" }, + { "predicate": { "unicopia:zap_cycle": 0.725}, "model": "unicopia:item/spectral_clock_fruiting_25" }, + { "predicate": { "unicopia:zap_cycle": 0.750}, "model": "unicopia:item/spectral_clock_fruiting_30" }, + { "predicate": { "unicopia:zap_cycle": 0.775}, "model": "unicopia:item/spectral_clock_fruiting_35" }, + { "predicate": { "unicopia:zap_cycle": 0.800}, "model": "unicopia:item/spectral_clock_ripe_00" }, + { "predicate": { "unicopia:zap_cycle": 0.825}, "model": "unicopia:item/spectral_clock_ripe_05" }, + { "predicate": { "unicopia:zap_cycle": 0.850}, "model": "unicopia:item/spectral_clock_ripe_10" }, + { "predicate": { "unicopia:zap_cycle": 0.875}, "model": "unicopia:item/spectral_clock_ripe_15" }, + { "predicate": { "unicopia:zap_cycle": 0.900}, "model": "unicopia:item/spectral_clock_ripe_20" }, + { "predicate": { "unicopia:zap_cycle": 0.925}, "model": "unicopia:item/spectral_clock_ripe_25" }, + { "predicate": { "unicopia:zap_cycle": 0.95}, "model": "unicopia:item/spectral_clock_ripe_30" }, + { "predicate": { "unicopia:zap_cycle": 0.975}, "model": "unicopia:item/spectral_clock_ripe_35" }, + { "predicate": { "unicopia:zap_cycle": 1.00}, "model": "unicopia:item/spectral_clock" } + ] +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_05.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_05.json new file mode 100644 index 00000000..e4214c69 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_05.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_05" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_10.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_10.json new file mode 100644 index 00000000..93494e06 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_10.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_10" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_15.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_15.json new file mode 100644 index 00000000..47fd5aae --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_15.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_15" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_20.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_20.json new file mode 100644 index 00000000..1f5e9555 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_20.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_20" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_25.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_25.json new file mode 100644 index 00000000..8bc63305 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_25.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_25" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_30.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_30.json new file mode 100644 index 00000000..a570c164 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_30.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_30" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_35.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_35.json new file mode 100644 index 00000000..847b370c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_35.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_35" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_00.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_00.json new file mode 100644 index 00000000..f2f18e39 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_00.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_00" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_05.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_05.json new file mode 100644 index 00000000..bb124dd8 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_05.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_05" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_10.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_10.json new file mode 100644 index 00000000..b4e54dd5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_10.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_10" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_15.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_15.json new file mode 100644 index 00000000..024df18b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_15.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_15" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_20.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_20.json new file mode 100644 index 00000000..86052382 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_20.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_20" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_25.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_25.json new file mode 100644 index 00000000..7a8b7bdc --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_25.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_25" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_30.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_30.json new file mode 100644 index 00000000..1be597da --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_30.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_30" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_35.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_35.json new file mode 100644 index 00000000..5feeec80 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_35.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_35" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_00.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_00.json new file mode 100644 index 00000000..5f8c076a --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_00.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_00" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_05.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_05.json new file mode 100644 index 00000000..d55c7a55 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_05.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_05" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_10.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_10.json new file mode 100644 index 00000000..b93324ef --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_10.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_10" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_15.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_15.json new file mode 100644 index 00000000..83965103 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_15.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_15" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_20.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_20.json new file mode 100644 index 00000000..de03836b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_20.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_20" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_25.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_25.json new file mode 100644 index 00000000..6bf6d0f4 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_25.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_25" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_30.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_30.json new file mode 100644 index 00000000..aa2b154c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_30.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_30" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_35.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_35.json new file mode 100644 index 00000000..9e16293d --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_35.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_35" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_00.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_00.json new file mode 100644 index 00000000..6fe39684 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_00.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_00" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_05.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_05.json new file mode 100644 index 00000000..f732e41b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_05.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_05" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_10.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_10.json new file mode 100644 index 00000000..025817fb --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_10.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_10" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_15.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_15.json new file mode 100644 index 00000000..3b1bf2a7 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_15.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_15" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_20.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_20.json new file mode 100644 index 00000000..b3f8251e --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_20.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_20" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_25.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_25.json new file mode 100644 index 00000000..3a0dbe6e --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_25.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_25" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_30.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_30.json new file mode 100644 index 00000000..17b96af7 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_30.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_30" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_35.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_35.json new file mode 100644 index 00000000..33f711fd --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_35.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_35" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_00.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_00.json new file mode 100644 index 00000000..047312c3 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_00.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_00" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_05.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_05.json new file mode 100644 index 00000000..6ebd0392 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_05.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_05" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_10.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_10.json new file mode 100644 index 00000000..07566221 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_10.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_10" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_15.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_15.json new file mode 100644 index 00000000..852542b1 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_15.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_15" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_20.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_20.json new file mode 100644 index 00000000..9a3d23d5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_20.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_20" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_25.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_25.json new file mode 100644 index 00000000..54944856 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_25.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_25" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_30.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_30.json new file mode 100644 index 00000000..c5b298ee --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_30.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_30" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_35.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_35.json new file mode 100644 index 00000000..6e5c32f8 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_35.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_35" + } +} diff --git a/src/main/resources/assets/unicopia/particles/bubble.json b/src/main/resources/assets/unicopia/particles/bubble.json new file mode 100644 index 00000000..21bb4eeb --- /dev/null +++ b/src/main/resources/assets/unicopia/particles/bubble.json @@ -0,0 +1,5 @@ +{ + "textures": [ + "minecraft:bubble" + ] +} diff --git a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh new file mode 100644 index 00000000..bdb8fec6 --- /dev/null +++ b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh @@ -0,0 +1,17 @@ +#version 150 + +#moj_import + +uniform sampler2D Sampler0; + +uniform vec4 ColorModulator; + +in vec4 texProj0; +in vec4 vertexColor; + +out vec4 fragColor; + +void main() { + vec4 scale = vec4(0.25, 0.25, 0.25, 0.25); + fragColor = textureProj(Sampler0, texProj0 * scale); +} diff --git a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json new file mode 100644 index 00000000..6744399f --- /dev/null +++ b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json @@ -0,0 +1,17 @@ +{ + "blend": { + "func": "add", + "srcrgb": "srcalpha", + "dstrgb": "1-srcalpha" + }, + "vertex": "unicopia:rendertype_portal_surface", + "fragment": "unicopia:rendertype_portal_surface", + "attributes": [], + "samplers": [ + { "name": "Sampler0" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] } + ] +} diff --git a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.vsh b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.vsh new file mode 100644 index 00000000..42aadd7b --- /dev/null +++ b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.vsh @@ -0,0 +1,19 @@ +#version 150 + +#moj_import + +in vec3 Position; +in vec4 Color; + +uniform mat4 ModelViewMat; +uniform mat4 ProjMat; + +out vec4 texProj0; +out vec4 vertexColor; + +void main() { + gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0); + + vertexColor = Color; + texProj0 = projection_from_position(gl_Position); +} diff --git a/src/main/resources/assets/unicopia/textures/block/curing_joke.png b/src/main/resources/assets/unicopia/textures/block/curing_joke.png new file mode 100644 index 00000000..1a0359ba Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/block/curing_joke.png differ diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage0.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage0.png new file mode 100644 index 00000000..a24793eb Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/block/gold_root_stage0.png differ diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage1.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage1.png new file mode 100644 index 00000000..f5830f64 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/block/gold_root_stage1.png differ diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage2.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage2.png new file mode 100644 index 00000000..b08acd77 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/block/gold_root_stage2.png differ diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage3.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage3.png new file mode 100644 index 00000000..816a7d1c Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/block/gold_root_stage3.png differ diff --git a/src/main/resources/assets/unicopia/textures/block/golden_oak_leaves.png b/src/main/resources/assets/unicopia/textures/block/golden_oak_leaves.png new file mode 100644 index 00000000..2242b168 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/block/golden_oak_leaves.png differ diff --git a/src/main/resources/assets/unicopia/textures/block/golden_oak_log.png b/src/main/resources/assets/unicopia/textures/block/golden_oak_log.png new file mode 100644 index 00000000..414dd04c Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/block/golden_oak_log.png differ diff --git a/src/main/resources/assets/unicopia/textures/block/golden_oak_log_top.png b/src/main/resources/assets/unicopia/textures/block/golden_oak_log_top.png new file mode 100644 index 00000000..be183711 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/block/golden_oak_log_top.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_angry.png b/src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_angry.png new file mode 100644 index 00000000..5f5cc603 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_angry.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_idle.png b/src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_idle.png new file mode 100644 index 00000000..7fe663ff Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_idle.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/poison_joke/tentacle.png b/src/main/resources/assets/unicopia/textures/entity/poison_joke/tentacle.png new file mode 100644 index 00000000..1026c1c0 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/poison_joke/tentacle.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/curing_joke.png b/src/main/resources/assets/unicopia/textures/item/curing_joke.png new file mode 100644 index 00000000..1fb7402a Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/curing_joke.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/golden_oak_sapling.png b/src/main/resources/assets/unicopia/textures/item/golden_oak_sapling.png new file mode 100644 index 00000000..2facd03b Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/golden_oak_sapling.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/golden_oak_seeds.png b/src/main/resources/assets/unicopia/textures/item/golden_oak_seeds.png new file mode 100644 index 00000000..27526d07 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/golden_oak_seeds.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_00.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_00.png new file mode 100644 index 00000000..8591477b Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_00.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_05.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_05.png new file mode 100644 index 00000000..6e74f8a4 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_05.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_10.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_10.png new file mode 100644 index 00000000..1cd94355 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_10.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_15.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_15.png new file mode 100644 index 00000000..7573eac7 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_15.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_20.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_20.png new file mode 100644 index 00000000..b6ca8164 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_20.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_25.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_25.png new file mode 100644 index 00000000..451a06e7 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_25.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_30.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_30.png new file mode 100644 index 00000000..92f6e428 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_30.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_35.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_35.png new file mode 100644 index 00000000..4d7b00ac Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_35.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_00.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_00.png new file mode 100644 index 00000000..31063a11 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_00.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_05.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_05.png new file mode 100644 index 00000000..11e64f71 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_05.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_10.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_10.png new file mode 100644 index 00000000..a69d288d Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_10.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_15.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_15.png new file mode 100644 index 00000000..d469774a Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_15.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_20.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_20.png new file mode 100644 index 00000000..698a20a7 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_20.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_25.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_25.png new file mode 100644 index 00000000..9ab9b43e Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_25.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_30.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_30.png new file mode 100644 index 00000000..de3e77a6 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_30.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_35.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_35.png new file mode 100644 index 00000000..ccbe99af Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_35.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_00.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_00.png new file mode 100644 index 00000000..9be87cb4 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_00.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_05.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_05.png new file mode 100644 index 00000000..3c147b7f Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_05.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_10.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_10.png new file mode 100644 index 00000000..6884c01b Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_10.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_15.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_15.png new file mode 100644 index 00000000..75124f0e Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_15.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_20.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_20.png new file mode 100644 index 00000000..78c13d32 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_20.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_25.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_25.png new file mode 100644 index 00000000..3f4048b6 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_25.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_30.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_30.png new file mode 100644 index 00000000..ec8b07b9 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_30.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_35.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_35.png new file mode 100644 index 00000000..246831ec Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_35.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_00.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_00.png new file mode 100644 index 00000000..54c1277e Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_00.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_05.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_05.png new file mode 100644 index 00000000..aea7f984 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_05.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_10.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_10.png new file mode 100644 index 00000000..a6ceabe3 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_10.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_15.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_15.png new file mode 100644 index 00000000..679026c9 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_15.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_20.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_20.png new file mode 100644 index 00000000..791560a9 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_20.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_25.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_25.png new file mode 100644 index 00000000..3b38257c Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_25.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_30.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_30.png new file mode 100644 index 00000000..37fa366a Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_30.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_35.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_35.png new file mode 100644 index 00000000..8716c5f9 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_35.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_00.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_00.png new file mode 100644 index 00000000..5e8c4f31 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_00.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_05.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_05.png new file mode 100644 index 00000000..c29b86d5 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_05.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_10.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_10.png new file mode 100644 index 00000000..561ea8b3 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_10.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_15.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_15.png new file mode 100644 index 00000000..eb56aed2 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_15.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_20.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_20.png new file mode 100644 index 00000000..24f86eae Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_20.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_25.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_25.png new file mode 100644 index 00000000..50eb8942 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_25.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_30.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_30.png new file mode 100644 index 00000000..9f228247 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_30.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_35.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_35.png new file mode 100644 index 00000000..e54583ea Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_35.png differ diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/change_race_hippogriff.png b/src/main/resources/assets/unicopia/textures/mob_effect/change_race_hippogriff.png new file mode 100644 index 00000000..057c2240 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/mob_effect/change_race_hippogriff.png differ diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/change_race_kirin.png b/src/main/resources/assets/unicopia/textures/mob_effect/change_race_kirin.png new file mode 100644 index 00000000..33be07ce Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/mob_effect/change_race_kirin.png differ diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_bat.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_bat.png new file mode 100644 index 00000000..77ef99b8 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_bat.png differ diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_changeling.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_changeling.png new file mode 100644 index 00000000..c29bf471 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_changeling.png differ diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_earth.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_earth.png new file mode 100644 index 00000000..c0d08791 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_earth.png differ diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_hippogriff.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_hippogriff.png new file mode 100644 index 00000000..f52bfd19 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_hippogriff.png differ diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_kirin.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_kirin.png new file mode 100644 index 00000000..ef61eebd Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_kirin.png differ diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_pegasus.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_pegasus.png new file mode 100644 index 00000000..56b4c3e0 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_pegasus.png differ diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_unicorn.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_unicorn.png new file mode 100644 index 00000000..b8151af3 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_unicorn.png differ diff --git a/src/main/resources/assets/unicopia/textures/spells/dark_vortex/accretion_disk.png b/src/main/resources/assets/unicopia/textures/spells/dark_vortex/accretion_disk.png new file mode 100644 index 00000000..d1ffe0c5 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/spells/dark_vortex/accretion_disk.png differ diff --git a/src/main/resources/data/c/tags/blocks/concrete_powders.json b/src/main/resources/data/c/tags/blocks/concrete_powders.json new file mode 100644 index 00000000..e3142856 --- /dev/null +++ b/src/main/resources/data/c/tags/blocks/concrete_powders.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_concrete_powder", + "minecraft:orange_concrete_powder", + "minecraft:magenta_concrete_powder", + "minecraft:light_blue_concrete_powder", + "minecraft:yellow_concrete_powder", + "minecraft:lime_concrete_powder", + "minecraft:pink_concrete_powder", + "minecraft:gray_concrete_powder", + "minecraft:light_gray_concrete_powder", + "minecraft:cyan_concrete_powder", + "minecraft:purple_concrete_powder", + "minecraft:blue_concrete_powder", + "minecraft:brown_concrete_powder", + "minecraft:green_concrete_powder", + "minecraft:red_concrete_powder", + "minecraft:black_concrete_powder" + ] +} diff --git a/src/main/resources/data/c/tags/items/boats.json b/src/main/resources/data/c/tags/items/boats.json new file mode 100644 index 00000000..dde2408d --- /dev/null +++ b/src/main/resources/data/c/tags/items/boats.json @@ -0,0 +1,15 @@ +{ + "replace": false, + "values": [ + "minecraft:oak_boat", + "minecraft:bamboo_raft", + "minecraft:spruce_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:mangrove_boat", + "minecraft:cherry_boat", + "unicopia:palm_boat" + ] +} diff --git a/src/main/resources/data/c/tags/items/concrete.json b/src/main/resources/data/c/tags/items/concrete.json new file mode 100644 index 00000000..c09d67ba --- /dev/null +++ b/src/main/resources/data/c/tags/items/concrete.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_concrete", + "minecraft:orange_concrete", + "minecraft:magenta_concrete", + "minecraft:light_blue_concrete", + "minecraft:yellow_concrete", + "minecraft:lime_concrete", + "minecraft:pink_concrete", + "minecraft:gray_concrete", + "minecraft:light_gray_concrete", + "minecraft:cyan_concrete", + "minecraft:purple_concrete", + "minecraft:blue_concrete", + "minecraft:brown_concrete", + "minecraft:green_concrete", + "minecraft:red_concrete", + "minecraft:black_concrete" + ] +} diff --git a/src/main/resources/data/c/tags/items/concrete_powders.json b/src/main/resources/data/c/tags/items/concrete_powders.json new file mode 100644 index 00000000..e3142856 --- /dev/null +++ b/src/main/resources/data/c/tags/items/concrete_powders.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_concrete_powder", + "minecraft:orange_concrete_powder", + "minecraft:magenta_concrete_powder", + "minecraft:light_blue_concrete_powder", + "minecraft:yellow_concrete_powder", + "minecraft:lime_concrete_powder", + "minecraft:pink_concrete_powder", + "minecraft:gray_concrete_powder", + "minecraft:light_gray_concrete_powder", + "minecraft:cyan_concrete_powder", + "minecraft:purple_concrete_powder", + "minecraft:blue_concrete_powder", + "minecraft:brown_concrete_powder", + "minecraft:green_concrete_powder", + "minecraft:red_concrete_powder", + "minecraft:black_concrete_powder" + ] +} diff --git a/src/main/resources/data/c/tags/items/foraging/edibles_filling.json b/src/main/resources/data/c/tags/items/foraging/edibles_filling.json index c183c3fb..bc799386 100644 --- a/src/main/resources/data/c/tags/items/foraging/edibles_filling.json +++ b/src/main/resources/data/c/tags/items/foraging/edibles_filling.json @@ -1,6 +1,8 @@ { "replace": false, "values": [ - { "id": "farmersdelight:horse_feed", "required": false } + { "id": "farmersdelight:horse_feed", "required": false }, + { "id": "farmersdelight:rice_bale", "required": false }, + { "id": "farmersdelight:straw_bale", "required": false } ] } diff --git a/src/main/resources/data/c/tags/items/glazed_terracotta.json b/src/main/resources/data/c/tags/items/glazed_terracotta.json new file mode 100644 index 00000000..02b98848 --- /dev/null +++ b/src/main/resources/data/c/tags/items/glazed_terracotta.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_glazed_terracotta", + "minecraft:orange_glazed_terracotta", + "minecraft:magenta_glazed_terracotta", + "minecraft:light_blue_glazed_terracotta", + "minecraft:yellow_glazed_terracotta", + "minecraft:lime_glazed_terracotta", + "minecraft:pink_glazed_terracotta", + "minecraft:gray_glazed_terracotta", + "minecraft:light_gray_glazed_terracotta", + "minecraft:cyan_glazed_terracotta", + "minecraft:purple_glazed_terracotta", + "minecraft:blue_glazed_terracotta", + "minecraft:brown_glazed_terracotta", + "minecraft:green_glazed_terracotta", + "minecraft:red_glazed_terracotta", + "minecraft:black_glazed_terracotta" + ] +} diff --git a/src/main/resources/data/c/tags/items/seeds.json b/src/main/resources/data/c/tags/items/seeds.json index d0c9c06a..d61f66fb 100644 --- a/src/main/resources/data/c/tags/items/seeds.json +++ b/src/main/resources/data/c/tags/items/seeds.json @@ -1,6 +1,8 @@ { "replace": false, "values": [ + "minecraft:pumpkin_seeds", + "minecraft:melon_seeds", "unicopia:oat_seeds", "unicopia:green_apple_seeds", "unicopia:sweet_apple_seeds", diff --git a/src/main/resources/data/dehydration/hydration_items/unicopia_food.json b/src/main/resources/data/dehydration/hydration_items/unicopia_food.json new file mode 100644 index 00000000..e9347cb6 --- /dev/null +++ b/src/main/resources/data/dehydration/hydration_items/unicopia_food.json @@ -0,0 +1,40 @@ +{ + "1": { + "replace": false, + "items": [ + "unicopia:banana", + "unicopia:rotten_apple" + ] + }, + "2": { + "replace": false, + "items": [ + "unicopia:green_apple", + "unicopia:sweet_apple", + "unicopia:sour_apple", + "unicopia:mango", + "unicopia:cider", + "unicopia:love_bottle", + "unicopia:love_mug" + ] + }, + "3": { + "replace": false, + "items": [ + "unicopia:pineapple", + "unicopia:rock_stew" + ] + }, + "4": { + "replace": false, + "items": [ + "unicopia:love_bucket" + ] + }, + "12": { + "replace": false, + "items": [ + "unicopia:juice" + ] + } +} diff --git a/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived.json b/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived.json new file mode 100644 index 00000000..fc16efe7 --- /dev/null +++ b/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived.json @@ -0,0 +1,30 @@ +{ + "replace": false, + "traits": "earth:2 life:1", + "items": [ + "#minecraft:planks", + "#minecraft:logs_that_burn", + "#minecraft:wooden_stairs", + "#minecraft:wooden_slabs", + "#minecraft:wooden_fences", + + "minecraft:sponge", + "minecraft:cobweb", + + "minecraft:kelp", + "minecraft:cake", + "minecraft:chorus_plant", + "minecraft:moss_carpet", + "minecraft:mossy_cobblestone", + + "minecraft:brown_mushroom_block", + "minecraft:red_mushroom_block", + "minecraft:mushroom_stem", + + "minecraft:slime_block", + "minecraft:honey_block", + "minecraft:honeycomb_block", + "minecraft:bee_nest", + "minecraft:beehive" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived_artificial.json b/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived_artificial.json new file mode 100644 index 00000000..72b34d4b --- /dev/null +++ b/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived_artificial.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "order:1 knowledge:3", + "items": [ + "#farmersdelight:canvas_signs", + "#farmersdelight:cabinets" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/blocks/dragon_immune.json b/src/main/resources/data/minecraft/tags/blocks/dragon_immune.json index 2daf1e1f..955d6bc1 100644 --- a/src/main/resources/data/minecraft/tags/blocks/dragon_immune.json +++ b/src/main/resources/data/minecraft/tags/blocks/dragon_immune.json @@ -1,6 +1,8 @@ { "replace": false, "values": [ - "unicopia:frosted_obsidian" + "unicopia:frosted_obsidian", + "unicopia:golden_oak_leaves", + "unicopia:golden_oak_log" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/blocks/leaves.json b/src/main/resources/data/minecraft/tags/blocks/leaves.json index 852058e8..ced25022 100644 --- a/src/main/resources/data/minecraft/tags/blocks/leaves.json +++ b/src/main/resources/data/minecraft/tags/blocks/leaves.json @@ -3,9 +3,11 @@ "values": [ "unicopia:palm_leaves", "unicopia:zap_leaves", + "unicopia:flowering_zap_leaves", "unicopia:green_apple_leaves", "unicopia:sweet_apple_leaves", "unicopia:sour_apple_leaves", + "unicopia:golden_oak_leaves", "unicopia:mango_leaves" ] } diff --git a/src/main/resources/data/minecraft/tags/blocks/logs.json b/src/main/resources/data/minecraft/tags/blocks/logs.json index 4e7dd5d0..f3a91e42 100644 --- a/src/main/resources/data/minecraft/tags/blocks/logs.json +++ b/src/main/resources/data/minecraft/tags/blocks/logs.json @@ -3,6 +3,7 @@ "values": [ "unicopia:palm_log", "unicopia:palm_wood", + "unicopia:golden_oak_log", "unicopia:stripped_palm_log", "unicopia:stripped_palm_wood", "unicopia:zap_log", diff --git a/src/main/resources/data/minecraft/tags/blocks/logs_that_burn.json b/src/main/resources/data/minecraft/tags/blocks/logs_that_burn.json index 38397972..31e49b86 100644 --- a/src/main/resources/data/minecraft/tags/blocks/logs_that_burn.json +++ b/src/main/resources/data/minecraft/tags/blocks/logs_that_burn.json @@ -3,6 +3,7 @@ "values": [ "unicopia:palm_log", "unicopia:palm_wood", + "unicopia:golden_oak_log", "unicopia:stripped_palm_log", "unicopia:stripped_palm_wood" ] diff --git a/src/main/resources/data/minecraft/tags/blocks/maintains_farmland.json b/src/main/resources/data/minecraft/tags/blocks/maintains_farmland.json index b8408162..2d5d0b68 100644 --- a/src/main/resources/data/minecraft/tags/blocks/maintains_farmland.json +++ b/src/main/resources/data/minecraft/tags/blocks/maintains_farmland.json @@ -7,6 +7,8 @@ "unicopia:oats_stem", "unicopia:green_apple_sprout", "unicopia:sweet_apple_sprout", - "unicopia:sour_apple_sprout" + "unicopia:sour_apple_sprout", + "unicopia:golden_oak_sprout", + "unicopia:gold_root" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json b/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json new file mode 100644 index 00000000..c6d84218 --- /dev/null +++ b/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json @@ -0,0 +1,16 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_leaves", + "unicopia:zap_leaves", + "unicopia:flowering_zap_leaves", + "unicopia:green_apple_leaves", + "unicopia:sweet_apple_leaves", + "unicopia:sour_apple_leaves", + "unicopia:golden_oak_leaves", + "unicopia:mango_leaves", + "unicopia:hay_block", + { "id": "unicopia:rice_block", "required": false }, + { "id": "unicopia:straw_block", "required": false } + ] +} diff --git a/src/main/resources/data/minecraft/tags/blocks/piglin_loved.json b/src/main/resources/data/minecraft/tags/blocks/piglin_loved.json new file mode 100644 index 00000000..22641dfc --- /dev/null +++ b/src/main/resources/data/minecraft/tags/blocks/piglin_loved.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "unicopia:golden_oak_leaves", + "unicopia:golden_oak_log", + "unicopia:golden_oak_sapling" + ] +} diff --git a/src/main/resources/data/minecraft/tags/items/chest_boats.json b/src/main/resources/data/minecraft/tags/items/chest_boats.json new file mode 100644 index 00000000..4ffee04d --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/chest_boats.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_chest_boat" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/wooden_fence_gates.json b/src/main/resources/data/minecraft/tags/items/fence_gates.json similarity index 100% rename from src/main/resources/data/minecraft/tags/items/wooden_fence_gates.json rename to src/main/resources/data/minecraft/tags/items/fence_gates.json diff --git a/src/main/resources/data/minecraft/tags/items/hanging_signs.json b/src/main/resources/data/minecraft/tags/items/hanging_signs.json new file mode 100644 index 00000000..2d324a64 --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/hanging_signs.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_hanging_sign" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/logs_that_burn.json b/src/main/resources/data/minecraft/tags/items/logs_that_burn.json new file mode 100644 index 00000000..4e7dd5d0 --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/logs_that_burn.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_log", + "unicopia:palm_wood", + "unicopia:stripped_palm_log", + "unicopia:stripped_palm_wood", + "unicopia:zap_log", + "unicopia:zap_wood", + "unicopia:stripped_zap_log", + "unicopia:stripped_zap_wood" + ] +} diff --git a/src/main/resources/data/minecraft/tags/items/piglin_loved.json b/src/main/resources/data/minecraft/tags/items/piglin_loved.json new file mode 100644 index 00000000..e34a469d --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/piglin_loved.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "unicopia:golden_oak_seeds", + "unicopia:golden_oak_leaves", + "unicopia:golden_oak_log", + "unicopia:golden_oak_sapling" + ] +} diff --git a/src/main/resources/data/minecraft/tags/items/saplings.json b/src/main/resources/data/minecraft/tags/items/saplings.json index c504b996..1be3e7f1 100644 --- a/src/main/resources/data/minecraft/tags/items/saplings.json +++ b/src/main/resources/data/minecraft/tags/items/saplings.json @@ -1,6 +1,11 @@ { "replace": false, "values": [ - "unicopia:mango_sapling" + "unicopia:mango_sapling", + "unicopia:palm_sapling", + "unicopia:green_apple_sapling", + "unicopia:sour_apple_sapling", + "unicopia:sweet_apple_sapling", + "unicopia:zapling" ] } diff --git a/src/main/resources/data/minecraft/tags/items/slabs.json b/src/main/resources/data/minecraft/tags/items/slabs.json new file mode 100644 index 00000000..56e1871c --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/slabs.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:chiselled_chitin_slab" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/stairs.json b/src/main/resources/data/minecraft/tags/items/stairs.json new file mode 100644 index 00000000..ddd6b7e3 --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/stairs.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:chiselled_chitin_stairs" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/tools.json b/src/main/resources/data/minecraft/tags/items/tools.json new file mode 100644 index 00000000..1a41feac --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/tools.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#unicopia:horse_shoes", + "#unicopia:polearms" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/wooden_doors.json b/src/main/resources/data/minecraft/tags/items/wooden_doors.json index e7071e6f..b00445b2 100644 --- a/src/main/resources/data/minecraft/tags/items/wooden_doors.json +++ b/src/main/resources/data/minecraft/tags/items/wooden_doors.json @@ -1,6 +1,8 @@ { "replace": false, "values": [ - "unicopia:palm_door" + "unicopia:palm_door", + "unicopia:dark_oak_stable_door", + "unicopia:stable_door" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_artificial_materials_from_ground.json b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_artificial_materials_from_ground.json index fb4ea397..401bed91 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_artificial_materials_from_ground.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_artificial_materials_from_ground.json @@ -2,23 +2,12 @@ "replace": false, "traits": "earth:1 chaos:1", "items": [ + "#minecraft:walls", + "#minecraft:slabs", + "#minecraft:stairs", "minecraft:cobblestone", - "minecraft:cobblestone_stairs", - "minecraft:cobblestone_slab", - "minecraft:cobblestone_wall", - "minecraft:mossy_cobblestone_wall", - "minecraft:mossy_cobblestone_stairs", - "minecraft:mossy_cobblestone_slab", - "minecraft:cobbled_deepslate_wall", - "minecraft:cobbled_deepslate_stairs", - "minecraft:cobbled_deepslate_slab", - "minecraft:sandstone_stairs", - "minecraft:sandstone", - "minecraft:chiseled_sandstone", - "minecraft:cut_sandstone", - "minecraft:sandstone_slab", - "minecraft:cut_sandstone_slab", - "minecraft:sandstone_wall", - "minecraft:smooth_sandstone_slab" + "#c:uncolored_sandstone_blocks", + "#c:uncolored_sandstone_stairs", + "#c:uncolored_sandstone_slabs" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json index aa98dbe1..a19e4901 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json @@ -2,56 +2,8 @@ "replace": false, "traits": "earth:1 order:1 knowledge:4", "items": [ - "minecraft:white_concrete", - "minecraft:orange_concrete", - "minecraft:magenta_concrete", - "minecraft:light_blue_concrete", - "minecraft:yellow_concrete", - "minecraft:lime_concrete", - "minecraft:pink_concrete", - "minecraft:gray_concrete", - "minecraft:light_gray_concrete", - "minecraft:cyan_concrete", - "minecraft:purple_concrete", - "minecraft:blue_concrete", - "minecraft:brown_concrete", - "minecraft:green_concrete", - "minecraft:red_concrete", - "minecraft:black_concrete", - - "minecraft:white_terracotta", - "minecraft:orange_terracotta", - "minecraft:magenta_terracotta", - "minecraft:light_blue_terracotta", - "minecraft:yellow_terracotta", - "minecraft:lime_terracotta", - "minecraft:pink_terracotta", - "minecraft:gray_terracotta", - "minecraft:light_gray_terracotta", - "minecraft:cyan_terracotta", - "minecraft:purple_terracotta", - "minecraft:blue_terracotta", - "minecraft:brown_terracotta", - "minecraft:green_terracotta", - "minecraft:red_terracotta", - "minecraft:black_terracotta", - "minecraft:terracotta", - - "minecraft:white_glazed_terracotta", - "minecraft:orange_glazed_terracotta", - "minecraft:magenta_glazed_terracotta", - "minecraft:light_blue_glazed_terracotta", - "minecraft:yellow_glazed_terracotta", - "minecraft:lime_glazed_terracotta", - "minecraft:pink_glazed_terracotta", - "minecraft:gray_glazed_terracotta", - "minecraft:light_gray_glazed_terracotta", - "minecraft:cyan_glazed_terracotta", - "minecraft:purple_glazed_terracotta", - "minecraft:blue_glazed_terracotta", - "minecraft:brown_glazed_terracotta", - "minecraft:green_glazed_terracotta", - "minecraft:red_glazed_terracotta", - "minecraft:black_glazed_terracotta" + "#minecraft:terracotta", + "#c:concrete", + "#c:glazed_terracotta" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/degraded_rocks_and_rock_derived.json b/src/main/resources/data/minecraft/traits/blocks/overworld/cracked_stone.json similarity index 100% rename from src/main/resources/data/minecraft/traits/blocks/overworld/degraded_rocks_and_rock_derived.json rename to src/main/resources/data/minecraft/traits/blocks/overworld/cracked_stone.json diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/dirt.json b/src/main/resources/data/minecraft/traits/blocks/overworld/dirt.json new file mode 100644 index 00000000..a77f37dd --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/dirt.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "earth:6", + "items": [ + "#minecraft:dirt" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/dirt_worked.json b/src/main/resources/data/minecraft/traits/blocks/overworld/dirt_worked.json new file mode 100644 index 00000000..118714f4 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/dirt_worked.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "traits": "earth:5 strength:3", + "items": [ + "minecraft:packed_mud", + "minecraft:decorated_pot", + "minecraft:reinforced_deepslate", + "minecraft:mud_bricks", + "minecraft:farmland" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/energised.json b/src/main/resources/data/minecraft/traits/blocks/overworld/energised.json new file mode 100644 index 00000000..9e26b500 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/energised.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "power:4", + "items": [ + "minecraft:coal_block" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/infested_stone.json b/src/main/resources/data/minecraft/traits/blocks/overworld/infested_stone.json new file mode 100644 index 00000000..4fd0a29b --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/infested_stone.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "traits": "life:2 blood:1 earth:6", + "items": [ + "minecraft:infested_cracked_stone_bricks", + "minecraft:infested_stone", + "minecraft:infested_stone_bricks", + "minecraft:infested_chiseled_stone_bricks", + "minecraft:infested_deepslate", + "minecraft:infested_cobblestone", + "minecraft:infested_mossy_stone_bricks" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_objects_studicious.json b/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_objects_studicious.json new file mode 100644 index 00000000..d37f0009 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_objects_studicious.json @@ -0,0 +1,10 @@ +{ + "replace": false, + "traits": "knowledge:9 life:3 water:9", + "items": [ + "minecraft:ochre_froglight", + "minecraft:verdant_froglight", + "minecraft:pearlescent_froglight", + "minecraft:frogspawn" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_worked.json b/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_worked.json new file mode 100644 index 00000000..7b9e89ae --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_worked.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "knowledge:9 order:3 strength:3", + "items": [ + "minecraft:chiseled_bookshelf" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/living_matter.json b/src/main/resources/data/minecraft/traits/blocks/overworld/living_matter.json index 93ab077f..5f51c2ae 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/living_matter.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/living_matter.json @@ -2,37 +2,16 @@ "replace": false, "traits": "earth:1 life:3", "items": [ - "minecraft:oak_sapling", - "minecraft:spruce_sapling", - "minecraft:birch_sapling", - "minecraft:acacia_sapling", - "minecraft:dark_oak_sapling", - "minecraft:oak_leaves", - "minecraft:spruce_leaves", - "minecraft:birch_leaves", - "minecraft:jungle_leaves", - "minecraft:acacia_leaves", - "minecraft:dark_oak_leaves", + "#minecraft:saplings", + "#minecraft:leaves", "minecraft:azalea_leaves", - "minecraft:flowering_azalea_leaves", + "#minecraft:flowers", "minecraft:grass", "minecraft:fern", "minecraft:azalea", "minecraft:flowering_azalea", "minecraft:seagrass", "minecraft:sea_pickle", - "minecraft:dandelion", - "minecraft:poppy", - "minecraft:blue_orchid", - "minecraft:allium", - "minecraft:azure_bluet", - "minecraft:red_tulip", - "minecraft:orange_tulip", - "minecraft:white_tulip", - "minecraft:pink_tulip", - "minecraft:oxeye_daisy", - "minecraft:cornflower", - "minecraft:lily_of_the_valley", "minecraft:wither_rose", "minecraft:spore_blossom", "minecraft:brown_mushroom", @@ -53,21 +32,9 @@ "minecraft:glow_berries", "minecraft:carrot", "minecraft:potato", - "minecraft:tube_coral_block", - "minecraft:brain_coral_block", - "minecraft:bubble_coral_block", - "minecraft:fire_coral_block", - "minecraft:horn_coral_block", - "minecraft:tube_coral", - "minecraft:brain_coral", - "minecraft:bubble_coral", - "minecraft:fire_coral", - "minecraft:horn_coral", - "minecraft:tube_coral_fan", - "minecraft:brain_coral_fan", - "minecraft:bubble_coral_fan", - "minecraft:fire_coral_fan", - "minecraft:horn_coral_fan", + "#c:corals", + "#c:coral_fans", + "#c:coral_blocks", "minecraft:dragon_egg", "minecraft:turtle_egg", "minecraft:glow_lichen", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/loose_materials_from_ground.json b/src/main/resources/data/minecraft/traits/blocks/overworld/loose_materials_from_ground.json index c77ab962..1e185154 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/loose_materials_from_ground.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/loose_materials_from_ground.json @@ -2,24 +2,9 @@ "replace": false, "traits": "earth:1 chaos:2 order:-1", "items": [ - "minecraft:sand", + "#minecraft:sand", "minecraft:gravel", - - "minecraft:white_concrete_powder", - "minecraft:orange_concrete_powder", - "minecraft:magenta_concrete_powder", - "minecraft:light_blue_concrete_powder", - "minecraft:yellow_concrete_powder", - "minecraft:lime_concrete_powder", - "minecraft:pink_concrete_powder", - "minecraft:gray_concrete_powder", - "minecraft:light_gray_concrete_powder", - "minecraft:cyan_concrete_powder", - "minecraft:purple_concrete_powder", - "minecraft:blue_concrete_powder", - "minecraft:brown_concrete_powder", - "minecraft:green_concrete_powder", - "minecraft:red_concrete_powder", - "minecraft:black_concrete_powder" + "minecraft:suspicious_gravel", + "#c:concrete_powders" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/materials_from_the_ground.json b/src/main/resources/data/minecraft/traits/blocks/overworld/materials_from_the_ground.json index 45d1f540..727e1b52 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/materials_from_the_ground.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/materials_from_the_ground.json @@ -12,7 +12,7 @@ "minecraft:clay", - "minecraft:coal_ore", + "minecraft:coal_ore", "minecraft:deepslate_coal_ore", "minecraft:iron_ore", "minecraft:deepslate_iron_ore", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/mechanical_with_organic_components.json b/src/main/resources/data/minecraft/traits/blocks/overworld/mechanical_with_organic_components.json index a7cabb62..f84cbc4a 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/mechanical_with_organic_components.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/mechanical_with_organic_components.json @@ -4,68 +4,23 @@ "items": [ "minecraft:tnt", "minecraft:trapped_chest", - "minecraft:shulker_box", - "minecraft:white_shulker_box", - "minecraft:orange_shulker_box", - "minecraft:magenta_shulker_box", - "minecraft:light_blue_shulker_box", - "minecraft:yellow_shulker_box", - "minecraft:lime_shulker_box", - "minecraft:pink_shulker_box", - "minecraft:gray_shulker_box", - "minecraft:light_gray_shulker_box", - "minecraft:cyan_shulker_box", - "minecraft:purple_shulker_box", - "minecraft:blue_shulker_box", - "minecraft:brown_shulker_box", - "minecraft:green_shulker_box", - "minecraft:red_shulker_box", - "minecraft:black_shulker_box", + "#c:shulker_boxes", "minecraft:ladder", "minecraft:bookshelf", "minecraft:sculk_sensor", - "minecraft:chest", + "#c:chests", "minecraft:crafting_table", "minecraft:hay_block", "minecraft:carved_pumpkin", "minecraft:jack_o_lantern", - - "minecraft:oak_button", - "minecraft:spruce_button", - "minecraft:birch_button", - "minecraft:jungle_button", - "minecraft:acacia_button", - "minecraft:dark_oak_button", - "minecraft:oak_pressure_plate", - "minecraft:spruce_pressure_plate", - "minecraft:birch_pressure_plate", - "minecraft:jungle_pressure_plate", - "minecraft:acacia_pressure_plate", - "minecraft:dark_oak_pressure_plate", + "#minecraft:wooden_buttons", + "#minecraft:wooden_pressure_plates", + "#minecraft:wooden_doors", + "#minecraft:wooden_trapdoors", + "#minecraft:fence_gates", - "minecraft:oak_door", - "minecraft:spruce_door", - "minecraft:birch_door", - "minecraft:jungle_door", - "minecraft:acacia_door", - "minecraft:dark_oak_door", - - "minecraft:oak_trapdoor", - "minecraft:spruce_trapdoor", - "minecraft:birch_trapdoor", - "minecraft:jungle_trapdoor", - "minecraft:acacia_trapdoor", - "minecraft:dark_oak_trapdoor", - - "minecraft:oak_fence_gate", - "minecraft:spruce_fence_gate", - "minecraft:birch_fence_gate", - "minecraft:jungle_fence_gate", - "minecraft:acacia_fence_gate", - "minecraft:dark_oak_fence_gate", - "minecraft:composter", "minecraft:barrel" ] diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_dead_matter.json b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_dead_matter.json index 8ed34693..dfe96c94 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_dead_matter.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_dead_matter.json @@ -19,12 +19,14 @@ "minecraft:dead_fire_coral_fan", "minecraft:dead_horn_coral_fan", "minecraft:dried_kelp_block", + "minecraft:bundle", "minecraft:skeleton_skull", "minecraft:wither_skeleton_skull", "minecraft:player_head", "minecraft:zombie_head", "minecraft:creeper_head", - "minecraft:dragon_head" + "minecraft:dragon_head", + "minecraft:piglin_head" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived.json b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived.json index fe8c9136..d59ad4e6 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived.json @@ -2,78 +2,20 @@ "replace": false, "traits": "earth:2 life:1", "items": [ - "minecraft:oak_leaves", - "minecraft:oak_planks", - "minecraft:oak_log", - "minecraft:oak_wood", - "minecraft:oak_slab", - "minecraft:oak_fence", - "minecraft:oak_stairs", - - "minecraft:spruce_leaves", - "minecraft:spruce_planks", - "minecraft:spruce_log", - "minecraft:spruce_wood", - "minecraft:spruce_slab", - "minecraft:spruce_fence", - "minecraft:spruce_stairs", - - "minecraft:birch_leaves", - "minecraft:birch_planks", - "minecraft:birch_log", - "minecraft:birch_wood", - "minecraft:birch_slab", - "minecraft:birch_fence", - "minecraft:birch_stairs", - - "minecraft:jungle_leaves", - "minecraft:jungle_planks", - "minecraft:jungle_log", - "minecraft:jungle_wood", - "minecraft:jungle_slab", - "minecraft:jungle_fence", - "minecraft:jungle_stairs", - - "minecraft:acacia_leaves", - "minecraft:acacia_planks", - "minecraft:acacia_log", - "minecraft:acacia_wood", - "minecraft:acacia_slab", - "minecraft:acacia_fence", - "minecraft:acacia_stairs", - - "minecraft:stripped_oak_log", - "minecraft:stripped_spruce_log", - "minecraft:stripped_birch_log", - "minecraft:stripped_jungle_log", - "minecraft:stripped_acacia_log", - - "minecraft:stripped_oak_wood", - "minecraft:stripped_spruce_wood", - "minecraft:stripped_birch_wood", - "minecraft:stripped_jungle_wood", - "minecraft:stripped_acacia_wood", + "#minecraft:planks", + "#minecraft:bamboo_blocks", + "#minecraft:logs_that_burn", + "#minecraft:wooden_stairs", + "#minecraft:wooden_slabs", + "#minecraft:wooden_fences", + "minecraft:bamboo_mosaic", + "minecraft:mangrove_roots", + "minecraft:bowl", + "minecraft:paper", "minecraft:sponge", "minecraft:cobweb", - "minecraft:white_wool", - "minecraft:orange_wool", - "minecraft:magenta_wool", - "minecraft:light_blue_wool", - "minecraft:yellow_wool", - "minecraft:lime_wool", - "minecraft:pink_wool", - "minecraft:gray_wool", - "minecraft:light_gray_wool", - "minecraft:cyan_wool", - "minecraft:purple_wool", - "minecraft:blue_wool", - "minecraft:brown_wool", - "minecraft:green_wool", - "minecraft:red_wool", - "minecraft:black_wool", - "minecraft:kelp", "minecraft:cake", "minecraft:chorus_plant", @@ -84,40 +26,6 @@ "minecraft:red_mushroom_block", "minecraft:mushroom_stem", - "minecraft:white_carpet", - "minecraft:orange_carpet", - "minecraft:magenta_carpet", - "minecraft:light_blue_carpet", - "minecraft:yellow_carpet", - "minecraft:lime_carpet", - "minecraft:pink_carpet", - "minecraft:gray_carpet", - "minecraft:light_gray_carpet", - "minecraft:cyan_carpet", - "minecraft:purple_carpet", - "minecraft:blue_carpet", - "minecraft:brown_carpet", - "minecraft:green_carpet", - "minecraft:red_carpet", - "minecraft:black_carpet", - - "minecraft:white_bed", - "minecraft:orange_bed", - "minecraft:magenta_bed", - "minecraft:light_blue_bed", - "minecraft:yellow_bed", - "minecraft:lime_bed", - "minecraft:pink_bed", - "minecraft:gray_bed", - "minecraft:light_gray_bed", - "minecraft:cyan_bed", - "minecraft:purple_bed", - "minecraft:blue_bed", - "minecraft:brown_bed", - "minecraft:green_bed", - "minecraft:red_bed", - "minecraft:black_bed", - "minecraft:slime_block", "minecraft:honey_block", "minecraft:honeycomb_block", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived_dark.json b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived_dark.json index f0020cac..97dac6f0 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived_dark.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived_dark.json @@ -4,7 +4,7 @@ "items": [ "minecraft:dark_oak_leaves", "minecraft:dark_oak_planks", - "minecraft:dark_oak_log", + "#minecraft:dark_oak_logs", "minecraft:dark_oak_stairs", "minecraft:stripped_dark_oak_log", "minecraft:stripped_dark_oak_wood", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/refined_ores.json b/src/main/resources/data/minecraft/traits/blocks/overworld/refined_ores.json index 12e85369..eeaaab67 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/refined_ores.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/refined_ores.json @@ -2,7 +2,7 @@ "replace": false, "traits": "strength:1 earth:1 knowledge:2", "items": [ - "minecraft:coal_block", + "#c:ores", "minecraft:emerald_block", "minecraft:iron_block", "minecraft:copper_block", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/rocks_and_rock_derived.json b/src/main/resources/data/minecraft/traits/blocks/overworld/rocks_and_rock_derived.json index 3c6a3758..0ba39e40 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/rocks_and_rock_derived.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/rocks_and_rock_derived.json @@ -27,6 +27,11 @@ "minecraft:smooth_sandstone", "minecraft:smooth_stone", + "#minecraft:stone_bricks", + "#minecraft:stone_tool_materials", + "#minecraft:stone_crafting_materials", + "#minecraft:walls", + "minecraft:brick_stairs", "minecraft:stone_brick_stairs", "minecraft:brick_wall", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/skulk.json b/src/main/resources/data/minecraft/traits/blocks/overworld/skulk.json new file mode 100644 index 00000000..0ba1791c --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/skulk.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "traits": "darkness:11 blood:1 earth:2", + "items": [ + "minecraft:sculk", + "minecraft:sculk_shrieker", + "minecraft:sculk_catalyst", + "minecraft:echo_shard", + "minecraft:sculk_vein" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/skulk_worked.json b/src/main/resources/data/minecraft/traits/blocks/overworld/skulk_worked.json new file mode 100644 index 00000000..01f152a9 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/skulk_worked.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "darkness:11 blood:1 earth:2 focus:18", + "items": [ + "minecraft:calibrated_sculk_sensor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/soft_and_kind.json b/src/main/resources/data/minecraft/traits/blocks/overworld/soft_and_kind.json new file mode 100644 index 00000000..e4848f3e --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/soft_and_kind.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "traits": "kindness:3", + "items": [ + "#minecraft:beds", + "#minecraft:wool", + "#minecraft:wool_carpets" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/animal_horns.json b/src/main/resources/data/minecraft/traits/items/overworld/animal_horns.json new file mode 100644 index 00000000..bf492b8f --- /dev/null +++ b/src/main/resources/data/minecraft/traits/items/overworld/animal_horns.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "strength:3 life:1 kindness:-1", + "items": [ + "minecraft:goat_horn" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/chaotic.json b/src/main/resources/data/minecraft/traits/items/overworld/chaotic.json index 0e1627c7..d6718786 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/chaotic.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/chaotic.json @@ -1,9 +1,7 @@ { "replace": false, - "traits": "chaos:5", + "traits": "chaos:15", "items": [ - "minecraft:gunpowder", - "minecraft:bow", - "minecraft:tnt_minecart" + "minecraft:string" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/chaotic_worked.json b/src/main/resources/data/minecraft/traits/items/overworld/chaotic_worked.json new file mode 100644 index 00000000..0e1627c7 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/items/overworld/chaotic_worked.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "traits": "chaos:5", + "items": [ + "minecraft:gunpowder", + "minecraft:bow", + "minecraft:tnt_minecart" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/clothing_bloody.json b/src/main/resources/data/minecraft/traits/items/overworld/clothing_bloody.json index 251f4f9c..28e5b60c 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/clothing_bloody.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/clothing_bloody.json @@ -7,6 +7,7 @@ "minecraft:leather_chestplate", "minecraft:leather_leggings", "minecraft:leather_boots", - "minecraft:leather_horse_armor" + "minecraft:leather_horse_armor", + "minecraft:rabbit_hide" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json index dda3eda8..e65442d1 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json @@ -2,14 +2,10 @@ "replace": false, "traits": "famine:-0.5 life:-1 knowledge:2", "items": [ - "minecraft:cooked_chicken", - "minecraft:cooked_beef", + "#c:cooked_meats", + "#c:cooked_fish", "minecraft:fermented_spider_eye", - "minecraft:cooked_mutton", - "minecraft:cooked_cod", - "minecraft:cooked_salmon", - "minecraft:cooked_porkchop", - "minecraft:cooked_rabbit", - "minecraft:rabbit_stew" + "#unicopia:food_types/cooked_fish", + "#unicopia:food_types/cooked_meat" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_fruits.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_fruits.json new file mode 100644 index 00000000..f5ec8152 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_fruits.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "kindness:3 happiness:9 water:2", + "items": [ + "c:fruits" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based.json index 8f31615b..a04d3f7e 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based.json @@ -15,6 +15,8 @@ "minecraft:baked_potato": "earth:2", "minecraft:poisonous_potato": "earth:2 poison:1", "minecraft:melon_slice": "life:1", - "minecraft:cookie": "happiness:6" + "minecraft:cookie": "happiness:6", + "#c:grain": "life:3 earth:3", + "#unicopia:food_types/raw_sea_vegitable": "life:2 water:10" } } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based_and_modified.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based_and_modified.json index 7b4d7df1..fbabbc6e 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based_and_modified.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based_and_modified.json @@ -5,6 +5,8 @@ "minecraft:golden_apple", "minecraft:enchanted_golden_apple", "minecraft:golden_carrot", - "minecraft:honey_bottle" + "minecraft:honey_bottle", + "#c:crops", + "#unicopia:food_types/forage_edible_filling" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json index 79609dc2..07371361 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json @@ -2,10 +2,6 @@ "replace": false, "traits": "blood:1 famine:-2", "items": [ - "minecraft:beef", - "minecraft:rabbit", - "minecraft:porkchop", - "minecraft:chicken", - "minecraft:mutton" + "#c:raw_meats" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_from_sea.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_from_sea.json index 4c4b41e4..a5a72f4e 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_from_sea.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_from_sea.json @@ -2,8 +2,7 @@ "replace": false, "traits": "life:1 famine:-2 water:5", "items": [ - "minecraft:cod", - "minecraft:salmon", - "minecraft:tropical_fish" + "#c:raw_fish", + "#unicopia:food_types/raw_fish" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_poisoned.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_poisoned.json index fb4e1c40..37031631 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_poisoned.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_poisoned.json @@ -3,6 +3,7 @@ "traits": "blood:1 famine:2 poison:4", "items": [ "minecraft:spider_eye", - "minecraft:rotten_flesh" + "minecraft:rotten_flesh", + "#unicopia:food_types/forage_nauseating" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/full_o_life.json b/src/main/resources/data/minecraft/traits/items/overworld/full_o_life.json index 86a1c75c..9f1ba84a 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/full_o_life.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/full_o_life.json @@ -2,14 +2,13 @@ "replace": false, "traits": "life:10 earth:2", "items": [ - "minecraft:pumpkin_seeds", - "minecraft:beetroot_seeds", - "minecraft:melon_seeds", + "#c:seeds", + "#minecraft:villager_plantable_seeds", "minecraft:cocoa_beans", "minecraft:egg", - "minecraft:wheat_seeds", "minecraft:wheat", "minecraft:bone_meal", - "minecraft:honeycomb" + "minecraft:honeycomb", + "minecraft:sniffer_egg" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/full_o_life_wet.json b/src/main/resources/data/minecraft/traits/items/overworld/full_o_life_wet.json index 95b180c3..95fc5874 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/full_o_life_wet.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/full_o_life_wet.json @@ -3,10 +3,6 @@ "traits": "water:5 life:5", "items": [ "minecraft:milk_bucket", - "minecraft:pufferfish_bucket", - "minecraft:salmon_bucket", - "minecraft:cod_bucket", - "minecraft:tropical_fish_bucket", - "minecraft:axolotl_bucket" + "#c:entity_water_buckets" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/full_o_water.json b/src/main/resources/data/minecraft/traits/items/overworld/full_o_water.json index 0b477073..c3e0b2ce 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/full_o_water.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/full_o_water.json @@ -2,6 +2,6 @@ "replace": false, "traits": "water:7", "items": [ - "minecraft:water_bucket" + "#c:water_buckets" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects.json b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects.json index 529835ec..4857a23d 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects.json @@ -4,22 +4,7 @@ "items": [ "minecraft:loom", "minecraft:lead", - "minecraft:writable_book", - "minecraft:white_dye", - "minecraft:orange_dye", - "minecraft:magenta_dye", - "minecraft:light_blue_dye", - "minecraft:yellow_dye", - "minecraft:lime_dye", - "minecraft:pink_dye", - "minecraft:gray_dye", - "minecraft:light_gray_dye", - "minecraft:cyan_dye", - "minecraft:purple_dye", - "minecraft:blue_dye", - "minecraft:brown_dye", - "minecraft:green_dye", - "minecraft:red_dye", - "minecraft:black_dye" + "#minecraft:bookshelf_books", + "#c:dyes" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_enchanted.json b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_enchanted.json index cb5498db..b54f4773 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_enchanted.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_enchanted.json @@ -5,6 +5,7 @@ "minecraft:experience_bottle", "minecraft:firework_rocket", "minecraft:firework_star", - "minecraft:enchanted_book" + "minecraft:enchanted_book", + "minecraft:recovery_compass" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_focused.json b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_focused.json index a99e32a6..032e7c35 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_focused.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_focused.json @@ -4,6 +4,8 @@ "items": [ "minecraft:potion", "minecraft:glass_bottle", - "minecraft:ender_eye" + "minecraft:ender_eye", + "#minecraft:trim_materials", + "minecraft:brush" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_studicious.json b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_studicious.json index 5b40675d..97e890e1 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_studicious.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_studicious.json @@ -2,6 +2,10 @@ "replace": false, "traits": "knowledge:9 order:3", "items": [ + "#minecraft:decorated_pot_sherds", + "#minecraft:trim_templates", + "#minecraft:signs", + "#minecraft:hanging_signs", "minecraft:flower_banner_pattern", "minecraft:creeper_banner_pattern", "minecraft:skull_banner_pattern", diff --git a/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived.json b/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived.json new file mode 100644 index 00000000..fc16efe7 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived.json @@ -0,0 +1,30 @@ +{ + "replace": false, + "traits": "earth:2 life:1", + "items": [ + "#minecraft:planks", + "#minecraft:logs_that_burn", + "#minecraft:wooden_stairs", + "#minecraft:wooden_slabs", + "#minecraft:wooden_fences", + + "minecraft:sponge", + "minecraft:cobweb", + + "minecraft:kelp", + "minecraft:cake", + "minecraft:chorus_plant", + "minecraft:moss_carpet", + "minecraft:mossy_cobblestone", + + "minecraft:brown_mushroom_block", + "minecraft:red_mushroom_block", + "minecraft:mushroom_stem", + + "minecraft:slime_block", + "minecraft:honey_block", + "minecraft:honeycomb_block", + "minecraft:bee_nest", + "minecraft:beehive" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived_artificial.json b/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived_artificial.json index e31599bf..3c81a2ee 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived_artificial.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived_artificial.json @@ -2,22 +2,7 @@ "replace": false, "traits": "order:1 knowledge:3", "items": [ - "minecraft:bowl", - "minecraft:string", - "minecraft:paper", - "minecraft:bundle", - "minecraft:oak_sign", - "minecraft:spruce_sign", - "minecraft:birch_sign", - "minecraft:jungle_sign", - "minecraft:acacia_sign", - "minecraft:dark_oak_sign", - "minecraft:oak_boat", - "minecraft:spruce_boat", - "minecraft:birch_boat", - "minecraft:jungle_boat", - "minecraft:acacia_boat", - "minecraft:dark_oak_boat", - "minecraft:rabbit_hide" + "#minecraft:chest_boats", + "#c:boats" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/special.json b/src/main/resources/data/minecraft/traits/items/overworld/special.json index e1ab5a93..b3a8525c 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/special.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/special.json @@ -15,6 +15,7 @@ "minecraft:slime_ball": "rot:2", "minecraft:ink_sac": "darkness:4", "minecraft:glow_ink_sac": "focus:1 chaos:3", - "minecraft:nautilus_shell": "water:6 life:3" + "minecraft:nautilus_shell": "water:6 life:3", + "minecraft:disc_fragment_5": "knowledge:1 darkness:1" } } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/tools.json b/src/main/resources/data/minecraft/traits/items/overworld/tools.json index 9799bed0..2c15bafb 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/tools.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/tools.json @@ -3,30 +3,7 @@ "traits": "order:-2 kindness:1 strength:3", "items": [ "minecraft:shears", - - "minecraft:wooden_shovel", - "minecraft:wooden_pickaxe", - "minecraft:wooden_axe", - "minecraft:wooden_hoe", - - "minecraft:stone_shovel", - "minecraft:stone_pickaxe", - "minecraft:stone_axe", - "minecraft:stone_hoe", - - "minecraft:golden_shovel", - "minecraft:golden_pickaxe", - "minecraft:golden_axe", - "minecraft:golden_hoe", - - "minecraft:iron_shovel", - "minecraft:iron_pickaxe", - "minecraft:iron_axe", - "minecraft:iron_hoe", - - "minecraft:diamond_shovel", - "minecraft:diamond_pickaxe", - "minecraft:diamond_axe", - "minecraft:diamond_hoe" + "#minecraft:tools", + "#c:tools" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/uplifting_trinkets.json b/src/main/resources/data/minecraft/traits/items/overworld/uplifting_trinkets.json index 7c7927a7..b0afe26c 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/uplifting_trinkets.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/uplifting_trinkets.json @@ -2,18 +2,6 @@ "replace": false, "traits": "happiness:7 focus:2", "items": [ - "minecraft:music_disc_13", - "minecraft:music_disc_cat", - "minecraft:music_disc_blocks", - "minecraft:music_disc_chirp", - "minecraft:music_disc_far", - "minecraft:music_disc_mall", - "minecraft:music_disc_mellohi", - "minecraft:music_disc_stal", - "minecraft:music_disc_strad", - "minecraft:music_disc_ward", - "minecraft:music_disc_11", - "minecraft:music_disc_wait", - "minecraft:music_disc_pigstep" + "#minecraft:music_discs" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/underworld/weapons.json b/src/main/resources/data/minecraft/traits/items/underworld/weapons.json index 8b700489..b47c5e8d 100644 --- a/src/main/resources/data/minecraft/traits/items/underworld/weapons.json +++ b/src/main/resources/data/minecraft/traits/items/underworld/weapons.json @@ -2,6 +2,7 @@ "replace": false, "traits": "order:-2 kindness:-3 strength:13 darkness:9 blood:4", "items": [ - "minecraft:netherite_sword" + "minecraft:netherite_sword", + "minecraft:netherite_upgrade_smithing_template" ] } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json b/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json new file mode 100644 index 00000000..dc70d6e7 --- /dev/null +++ b/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json @@ -0,0 +1,36 @@ +{ + "parent": "unicopia:unicopia/earth/born_on_a_rock_farm", + "display": { + "icon": { + "item": "unicopia:iron_horse_shoe" + }, + "title": { + "translate": "advancements.unicopia.dead_ringer.title" + }, + "description": { + "translate": "advancements.unicopia.dead_ringer.description" + }, + "frame": "task", + "show_toast": true, + "announce_to_chat": true, + "hidden": true + }, + "criteria": { + "killed_entity_with_horseshoe": { + "trigger": "minecraft:player_killed_entity", + "conditions": { + "killing_blow": { + "tags": [ + { + "id": "unicopia:from_horseshoes", + "expected": true + } + ] + } + } + } + }, + "requirements": [ + [ "killed_entity_with_horseshoe" ] + ] +} diff --git a/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json b/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json index 881e8c8d..083c3c04 100644 --- a/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json +++ b/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json @@ -33,4 +33,4 @@ "requirements": [ [ "killed_entity_with_rock" ] ] -} +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/diets/races/changeling.json b/src/main/resources/data/unicopia/diets/races/changeling.json index e6255344..8b108468 100644 --- a/src/main/resources/data/unicopia/diets/races/changeling.json +++ b/src/main/resources/data/unicopia/diets/races/changeling.json @@ -46,7 +46,7 @@ "amplifier": 1 }, { - "type": "unicopia:multiply_hunger", + "type": "unicopia:lose_hunger", "multiplier": 0.5 } ] @@ -62,7 +62,7 @@ "effects": [ { "name": "Love Consumption", - "type": "unicopia:clear_love_sickness" + "type": "unicopia:cure_love_sickness" } ] } @@ -81,7 +81,7 @@ }, { "name": "unicopia.affliction.love_sickness", - "type": "unicopia:multiply_hunger", + "type": "unicopia:lose_hunger", "multiplier": 0.5 } ] @@ -122,7 +122,7 @@ "amplifier": 1 }, { - "type": "unicopia:multiply_hunger", + "type": "unicopia:lose_hunger", "multiplier": 0.5 } ] diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/curing_joke.json b/src/main/resources/data/unicopia/loot_tables/blocks/curing_joke.json new file mode 100644 index 00000000..7a4cba95 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/curing_joke.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1.0, + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "unicopia:curing_joke" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/darK_oak_stable_door.json b/src/main/resources/data/unicopia/loot_tables/blocks/dark_oak_stable_door.json similarity index 82% rename from src/main/resources/data/unicopia/loot_tables/blocks/darK_oak_stable_door.json rename to src/main/resources/data/unicopia/loot_tables/blocks/dark_oak_stable_door.json index f758c3e2..eb337eb8 100644 --- a/src/main/resources/data/unicopia/loot_tables/blocks/darK_oak_stable_door.json +++ b/src/main/resources/data/unicopia/loot_tables/blocks/dark_oak_stable_door.json @@ -13,14 +13,14 @@ "type": "minecraft:item", "conditions": [ { - "block": "unicopia:darK_oak_stable_door", + "block": "unicopia:dark_oak_stable_door", "condition": "minecraft:block_state_property", "properties": { "half": "lower" } } ], - "name": "unicopia:darK_oak_stable_door" + "name": "unicopia:dark_oak_stable_door" } ], "rolls": 1.0 diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json b/src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json new file mode 100644 index 00000000..6bfe9e77 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json @@ -0,0 +1,51 @@ +{ + "type": "minecraft:block", + "functions": [ + { + "function": "minecraft:explosion_decay" + } + ], + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:golden_carrot" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "age": "7" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:golden_carrot" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecraft:blocks/carrots" +} diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/golden_apple.json b/src/main/resources/data/unicopia/loot_tables/blocks/golden_apple.json new file mode 100644 index 00000000..7bd87a25 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/golden_apple.json @@ -0,0 +1,49 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1.0, + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:golden_apple" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + }, + { + "block": "unicopia:golden_apple", + "condition": "minecraft:block_state_property", + "properties": { + "enchanted": "false" + } + } + ] + }, + { + "rolls": 1.0, + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:enchanted_golden_apple" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + }, + { + "block": "unicopia:golden_apple", + "condition": "minecraft:block_state_property", + "properties": { + "enchanted": "true" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_leaves.json b/src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_leaves.json new file mode 100644 index 00000000..62c30a92 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_leaves.json @@ -0,0 +1,116 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:alternatives", + "children": [ + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecraft:any_of", + "terms": [ + { + "condition": "minecraft:match_tool", + "predicate": { + "items": [ + "minecraft:shears" + ] + } + }, + { + "condition": "minecraft:match_tool", + "predicate": { + "enchantments": [ + { + "enchantment": "minecraft:silk_touch", + "levels": { + "min": 1 + } + } + ] + } + } + ] + } + ], + "name": "unicopia:golden_oak_leaves" + } + ] + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:inverted", + "term": { + "condition": "minecraft:any_of", + "terms": [ + { + "condition": "minecraft:match_tool", + "predicate": { + "items": [ + "minecraft:shears" + ] + } + }, + { + "condition": "minecraft:match_tool", + "predicate": { + "enchantments": [ + { + "enchantment": "minecraft:silk_touch", + "levels": { + "min": 1 + } + } + ] + } + } + ] + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "conditions": [ + { + "chances": [ + 0.02, + 0.022222223, + 0.025, + 0.033333335, + 0.1 + ], + "condition": "minecraft:table_bonus", + "enchantment": "minecraft:fortune" + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + }, + { + "function": "minecraft:explosion_decay" + } + ], + "name": "minecraft:blaze_rod" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_log.json b/src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_log.json new file mode 100644 index 00000000..63a2ecb4 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_log.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1.0, + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "unicopia:golden_oak_log" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_sapling.json b/src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_sapling.json new file mode 100644 index 00000000..d97e4dfe --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_sapling.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1.0, + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "unicopia:golden_oak_sapling" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json new file mode 100644 index 00000000..1101857f --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json @@ -0,0 +1,157 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:hay_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:hay_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:hay_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:hay_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:hay_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:hay_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:hay_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:hay_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json new file mode 100644 index 00000000..20a3852d --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json @@ -0,0 +1,157 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json new file mode 100644 index 00000000..ab4fa683 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json @@ -0,0 +1,157 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json b/src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json new file mode 100644 index 00000000..2dac1a86 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json @@ -0,0 +1,204 @@ +{ + "type": "minecraft:chest", + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_earth" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 3 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_unicorn" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_pegasus" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_bat" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_kirin" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_hippogriff" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_earth" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 5 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_unicorn" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_pegasus" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_bat" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_kirin" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_hippogriff" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + } + ], + "rolls": 6.0 + } + ] +} diff --git a/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json b/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json new file mode 100644 index 00000000..7d41de39 --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json @@ -0,0 +1,13 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "##", + "##" + ], + "key": { + "#": [ + { "item": "unicopia:dense_cloud" } + ] + }, + "result": { "item": "unicopia:cloud_door", "count": 1 } +} diff --git a/src/main/resources/data/unicopia/recipes/gold_nugget.json b/src/main/resources/data/unicopia/recipes/gold_nugget.json new file mode 100644 index 00000000..5b393a11 --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/gold_nugget.json @@ -0,0 +1,9 @@ +{ + "type": "smelting", + "ingredient": { + "item": "unicopia:golden_oak_seeds" + }, + "result": "minecraft:gold_nugget", + "experience": 0.2, + "cookingtime": 10 +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/growing/curing_joke.json b/src/main/resources/data/unicopia/recipes/growing/curing_joke.json new file mode 100644 index 00000000..61625b41 --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/curing_joke.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:cornflower", + "consume": { + "Name": "minecraft:lapis_block", + "Properties": {} + }, + "output": { + "Name": "unicopia:curing_joke", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/growing/gold_root.json b/src/main/resources/data/unicopia/recipes/growing/gold_root.json new file mode 100644 index 00000000..a6d2d09b --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/gold_root.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:carrots", + "consume": { + "Name": "minecraft:raw_gold_block", + "Properties": {} + }, + "output": { + "Name": "unicopia:gold_root", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/growing/golden_oak_sapling.json b/src/main/resources/data/unicopia/recipes/growing/golden_oak_sapling.json new file mode 100644 index 00000000..b129e3ae --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/golden_oak_sapling.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:oak_sapling", + "consume": { + "Name": "minecraft:raw_gold_block", + "Properties": {} + }, + "output": { + "Name": "unicopia:golden_oak_sapling", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/growing/plunder_vine.json b/src/main/resources/data/unicopia/recipes/growing/plunder_vine.json new file mode 100644 index 00000000..df6a6eac --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/plunder_vine.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:wither_rose", + "consume": { + "Name": "minecraft:netherrack", + "Properties": {} + }, + "output": { + "Name": "unicopia:plunder_vine_bud", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/growing/zapling.json b/src/main/resources/data/unicopia/recipes/growing/zapling.json new file mode 100644 index 00000000..971c2099 --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/zapling.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:dark_oak_sapling", + "consume": { + "Name": "unicopia:chitin", + "Properties": {} + }, + "output": { + "Name": "unicopia:zapling", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/palm_planks.json b/src/main/resources/data/unicopia/recipes/palm_planks.json index c8a7dea1..2e94f502 100644 --- a/src/main/resources/data/unicopia/recipes/palm_planks.json +++ b/src/main/resources/data/unicopia/recipes/palm_planks.json @@ -1,7 +1,9 @@ { "type": "minecraft:crafting_shapeless", + "category": "building", + "group": "planks", "ingredients": [ - { "item": "unicopia:palm_log" } + { "tag": "unicopia:palm_logs" } ], "result": { "item": "unicopia:palm_planks", diff --git a/src/main/resources/data/unicopia/recipes/palm_wood.json b/src/main/resources/data/unicopia/recipes/palm_wood.json index 0c28e123..2d4c9008 100644 --- a/src/main/resources/data/unicopia/recipes/palm_wood.json +++ b/src/main/resources/data/unicopia/recipes/palm_wood.json @@ -1,5 +1,7 @@ { "type": "minecraft:crafting_shaped", + "category": "building", + "group": "bark", "pattern": [ "##", "##" @@ -10,6 +12,7 @@ } }, "result": { + "count": 3, "item": "unicopia:palm_wood" } } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/stripped_palm_wood.json b/src/main/resources/data/unicopia/recipes/stripped_palm_wood.json new file mode 100644 index 00000000..1b4f818e --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/stripped_palm_wood.json @@ -0,0 +1,18 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "building", + "group": "bark", + "pattern": [ + "##", + "##" + ], + "key": { + "#": { + "item": "unicopia:stripped_palm_log" + } + }, + "result": { + "count": 3, + "item": "unicopia:stripped_palm_wood" + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json index 9c416f4b..d552314e 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json @@ -5,68 +5,68 @@ "content": { "pages": [ { - "title": "Ch.4 Air Magic", + "title": "gui.unicopia.spellbook.chapter.air.p1.title", "level": 1, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/air_magic.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/air_magic.png" } ] }, { - "title": "1st Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p2.title", "level": 1, "elements": [ - "A new month, a new chapter. Little Luna was getting bored of sitting in the tower all day (and who's to blame her? We've been on this assignment for over two months at this point).", - "So as a little treat, we've decided to go on a little trip to the Grand Marepid Falls to visit my friend, Commander Hurricane." + "gui.unicopia.spellbook.chapter.air.p1.1.body", + "gui.unicopia.spellbook.chapter.air.p1.2.body" ] }, { "title": "", "level": 1, "elements": [ - "The Commander has also very graciously allowed me access to her library to continue my studies. I'm excited to see what combining unicorn and pegasus magics might bring.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p1.3.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "2nd Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p3.title", "level": 0, "elements": [ - "Apologies for the, um, unusual entry in the appendices for today. It appears some little gremlin managed to obscond with my journal.", - "At the princess' behest, so dreadfully sorry", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p3.1.body", + "gui.unicopia.spellbook.author1.sign_off.b", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Air Magic I", + "title": "gui.unicopia.spellbook.chapter.air.p4.title", "level": 3, "elements": [ - "Pegasus Magic", - "Air magic is to pegasi like fire is to unicorns. They're both equally hard to control but where fire is primarily focused around force, destruction, or protection, air is all about flexibility and free motion.", - "Command Hurricane has very kindly given me some tips on how to identify this trait in everyday objects.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + "gui.unicopia.spellbook.chapter.air.p4.1.body", + "gui.unicopia.spellbook.chapter.air.p4.2.body", + "gui.unicopia.spellbook.chapter.air.p4.3.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { "title": "", "level": 3, "elements": [ - "Anything relating to flight, or that originated from creatures that fly, or that comes from up above can be considered a source of the air trait.", - "Eg. Feathers.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p4.4.body", + "gui.unicopia.spellbook.chapter.air.p4.5.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Catapult", + "title": "spell.unicopia.catapult", "level": 4, "elements": [ - "This is a straightforward application of a unicorn's telekineses. The catapult gem allows a caster to grab any block or creature and fling them into the air.", - "Use it again on something already thrown to push it away from you.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + "gui.unicopia.spellbook.chapter.air.catapult.1.body", + "gui.unicopia.spellbook.chapter.air.catapult.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -74,7 +74,7 @@ "level": 4, "elements": [ { "recipe": "unicopia:spells/catapult" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:flame" }, @@ -82,17 +82,17 @@ { "count": 9, "trait": "unicopia:air" } ] }, - "* One can add apply more force by adding the strength trait" + "gui.unicopia.spellbook.chapter.air.catapult.modifier.1" ] }, { - "title": "Bubble", + "title": "spell.unicopia.bubble", "level": 2, "elements": [ - "A defensive and utility spell. Bubble will trap the target in a giant soap bubble, rendering them defensless.", - "Use it again will pop the bubble.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/water.png" } + "gui.unicopia.spellbook.chapter.air.bubble.1.body", + "gui.unicopia.spellbook.chapter.air.bubble.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/water.png" } ] }, { @@ -100,7 +100,7 @@ "level": 2, "elements": [ { "recipe": "unicopia:spells/bubble" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:catapult" }, @@ -111,46 +111,46 @@ ] }, { - "title": "8th Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p7.title", "level": 4, "elements": [ - "I thought I would take a short moment to write down an entry to record my findings whilst Luna and The Commander are out.", - "Pegasi magic really is a fascinating beast. It's not like our magic, which is more of a study, with rigid rules and practices." + "gui.unicopia.spellbook.chapter.air.p7.1.body", + "gui.unicopia.spellbook.chapter.air.p7.2.body" ] }, { - "title": "Pegasi", + "title": "gui.unicopia.spellbook.chapter.air.p8.title", "level": 5, "elements": [ - "Pegasus magic is more about feeling. It's an art form. They don't think about what they want to do, it just happens, but it all still follows the same principle." + "gui.unicopia.spellbook.chapter.air.p8.1.body" ] }, { - "title": "Pegasi II", + "title": "gui.unicopia.spellbook.chapter.air.p9.title", "level": 6, "elements": [ - "Take for example their cloud homes. There are no spells I can read that would let me do this, but if you feel, not with your hooves or your horn, but properly, with your mind, your heart, you will find magic buzzing all throughout.", - "Clouds are teeming with the air and water trait, but also others, like power, strength, life, earth. All traits of the material the clouds are mimicking." + "gui.unicopia.spellbook.chapter.air.p9.1.body", + "gui.unicopia.spellbook.chapter.air.p9.2.body" ] }, { - "title": "Pegasi III", + "title": "gui.unicopia.spellbook.chapter.air.p10.title", "level": 7, "elements": [ - "I wish I could study this further, but I'm afraid to interfere in this magic I don't yet fully understand.", - "Commander Hurricane may never forgive me if I destroy her home the first time I'm here.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p10.1.body", + "gui.unicopia.spellbook.chapter.air.p10.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Feather Falling", + "title": "spell.unicopia.feather_fall", "level": 5, "elements": [ - "Expanding on the defensive capabilities of the protection gem, I've attempted to extend its advantages to party members.", - "This one is unusual because of its complexity, but in theory it should allow one to slow their own and friends' descent.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + "gui.unicopia.spellbook.chapter.air.feather_fall.1.body", + "gui.unicopia.spellbook.chapter.air.feather_fall.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -158,7 +158,7 @@ "level": 5, "elements": [ { "recipe": "unicopia:spells/feather_fall" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:shield" }, @@ -171,68 +171,47 @@ ] }, { - "title": "10th Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p12.title", "level": 6, "elements": [ - "Went to dinner with Commander Huricane and Luna. We got to talking about architecture and Hurricane mentioned the Taz Marehall.", - "Luna thought it was a rather funny name of a castle. I had to remind her that not all cultures are the same.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p12.1.body", + "gui.unicopia.spellbook.chapter.air.p12.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "21st Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p13.title", "level": 7, "elements": [ - { - "text": "I'm writing this on the eve of my return to", - "extra": [ - { "text": "Trotholm.", "obfuscated": true }, - "Though my time in Cloudopolis has been elightening, I look forward to a return to the familiar surroundings and a proper rest in my own solid bed." - ] - }, - "I cannot say the same for Luna, though. She is currently sitting on my bed beside me pouting over every little thing she sees me put into my saddlebag." + "gui.unicopia.spellbook.chapter.air.p13.1.body", + "gui.unicopia.spellbook.chapter.air.p13.2.body" ] }, { - "title": "Returning Home", + "title": "gui.unicopia.spellbook.chapter.air.p14.title", "level": 7, "elements": [ - { - "text": "She keeps insisting that we stay a little longer", - "extra": [ - { "text": "to hang out with that pegasus colt I saw her with the other day, no doubt", "strikethrough": true} - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p14.1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "22nd Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p15.title", "level": 8, "elements": [ - { - "text": "As we're flying above the mountain tops, I can't help but feel in awe the beauty that", - "extra": [ - { "text": "Mother Faust", "obfuscated": true}, - "has given us." - ] - }, - "I had to spend the whole time keeping Luna from falling out, and of course answering questions about all the different kinds of clouds. Thankfully, the balloon operator was there to help with the subtleties." + "gui.unicopia.spellbook.chapter.air.p15.1.body", + "gui.unicopia.spellbook.chapter.air.p15.2.body" ] }, { "title": "", "level": 8, "elements": [ - "Being out here, above all of our troubles, really makes me forget the rest of the world for a moment. It takes me back to a simpler time, before the--", - "Of course the flashes of explosions to the west has to ruin it all. There appears to be a storm on the horizon. I certainly hope it not to be a bad omen..." + "gui.unicopia.spellbook.chapter.air.p15.3.body", + "gui.unicopia.spellbook.chapter.air.p15.4.body" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json b/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json index 707f95b8..602110cb 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json @@ -5,60 +5,46 @@ "content": { "pages": [ { - "title": "Ch.7 Artefacts", + "title": "gui.unicopia.spellbook.chapter.artefacts.p1.title", "level": 0, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/crystal_heart.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/crystal_heart.png" } ] }, { - "title": "2nd Mare '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.p2.title", "level": 0, "elements": [ - "What follows is a compendium of research into certain objects of interest identified through my research.", - "Not all of these have gotten anywhere, as they have been proven to either be pure legend, or perhaps a hoax, so I cannot put stock into their stories.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.artefacts.p2.1.body", + "gui.unicopia.spellbook.chapter.artefacts.p2.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "The Crystal Heart", + "title": "item.unicopia.crystal_heart", "level": 0, "elements": [ { "item": { "item": "unicopia:crystal_heart" } }, - "Status: Lost", - "Crafted by a group of unicorns long ago, its origin and current location is unknown.", - "What few accounts exist claim it was a powerful tool of protection and support, as it would funnel life force from enemies towards the caster and their allies." + "gui.unicopia.spellbook.chapter.artefacts.status.lost", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.1.body", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.2.body" ] }, { - "title": "5th Mare '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.title", "level": 0, "elements": [ - "Other accounts say that this artefact only functions when mounted on a specific pedestal of diamond blocks, like a beacon." + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.3.body" ] }, { - "title": "Torn Page", + "title": "gui.unicopia.spellbook.chapter.artefacts.torn_page.title", "level": 0, "elements": [ - { - "text": "", - "extra": [ - { "text": "aaa", "obfuscated": "true" }, - { "text": "ential c" }, - { "text": "a", "obfuscated": "true" }, - { "text": "r" }, - { "text": "aaa", "obfuscated": "true" }, - { "text": "nial purpose" }, - { "text": "aaa", "obfuscated": "true" }, - { "text": "?" } - ] - }, - { - "text": "Aasa sasa fwefsd q43rgfd wqklmsdfl as, klasn.", "obfuscated": "true" - }, - "Building Materials:", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.1.body", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.2.body", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.3.body", { "ingredients": [ { "count": 2, "item": "minecraft:end_rod" }, @@ -69,7 +55,7 @@ ] }, { - "title": "Crystal Podium", + "title": "gui.unicopia.spellbook.chapter.artefacts.crystal_podium.title", "level": 0, "elements": [ { "x": 40, "item": { "item": "minecraft:diamond_block" } }, @@ -86,134 +72,134 @@ ] }, { - "title": "Dragon's Breath Scroll", + "title": "item.unicopia.dragon_breath_scroll", "level": 0, "elements": [ { "item": { "item": "unicopia:dragon_breath_scroll" } }, - "Status: Confirmed", - "It's, um a scroll that you write somepony's name on it and you hold it in one hoof and something in the other hoof and, like, um it goes whooosh and the item is sent to that pony.", - "- XOXOX Lulu" + "gui.unicopia.spellbook.chapter.artefacts.status.confirmed", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.2.body", + "gui.unicopia.spellbook.author3.name" ] }, { - "title": "2nd Hoof '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.title", "level": 0, "elements": [ - "P.S. Uncle Starswirly is a dunderhead.", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.3.body", { "recipe": "unicopia:dragon_breath_scroll" } ] }, { - "title": "Bangle of Comradery", + "title": "item.unicopia.friendship_bracelet", "level": 0, "elements": [ { "item": { "item": "unicopia:friendship_bracelet" } }, - "Status: Confirmed", - "Used in the past by spellcasters to communicate over long distances, this band has long since lost its old use.", - "Unicorns who sign and hand out this band can use it to share certain magic effects with their friends." + "gui.unicopia.spellbook.chapter.artefacts.status.confirmed", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.1.body", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.2.body" ] }, { - "title": "13th Mare '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.title", "level": 0, "elements": [ - "Anyone wearing a bangle you have signed will be able to benefit from the positive effects of your spells, or will be allowed through protection and shield spells.", - "Mana costs are also shared equally between all nearby members." + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.3.body", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.4.body" ] }, { - "title": "Wings of Icarus", + "title": "item.unicopia.pegasus_amulet", "level": 0, "elements": [ { "item": { "item": "unicopia:pegasus_amulet" } }, - "Status: Lost", - "Commander Hurricane informed me of this, though I've found little texts to back up his claims.", - "The Pegasus Amulet is claimed to grant the wearer temporary flight, like a pegasus." + "gui.unicopia.spellbook.chapter.artefacts.status.lost", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.1.body", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.2.body" ] }, { - "title": "21st Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.title", "level": 0, "elements": [ - "It was intended as an aide for early unicorn ambassadors to Cloud Heights, but was lost after negotiations broke down." + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.3.body" ] }, { - "title": "Meadowbrook's Staff", + "title": "item.unicopia.meadowbrooks_staff", "level": 3, "elements": [ { "item": { "item": "unicopia:meadowbrooks_staff" } }, - "Status: Confirmed", - "A precursor to magic staffs, the meadwobrook's staff is an upright support structure commonly used by warlocks during long incantation sessions.", - "It features a twisting and mottled shape with a dense and sturdy core capable of supporting the weight of an average-sized, adult male." + "gui.unicopia.spellbook.chapter.artefacts.status.confirmed", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.1.body", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.2.body" ] }, { - "title": "22nd Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.title", "level": 3, "elements": [ - "Due to its dense structure and flamability, this object also serves a secondary purpose as an offensive weapon and fuel source should the situation demand.", - "To use correctly in combat, one must begin by gripping the staff by the narrow end in both hands, followed by a swift swing from above one's head whilst yelling 'Fus Roh DAH!'" + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.3.body", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.4.body" ] }, { - "title": "Magic Staffs", + "title": "item.unicopia.magic_staff", "level": 5, "elements": [ { "item": { "item": "unicopia:magic_staff" } }, - "Status: Unconfirmed", - "Magical aides for non-magical users. Magic staffs work in a similar way to a unicorns horn in that they can be used to channel and harness the innate magic stored within gems." + "gui.unicopia.spellbook.chapter.artefacts.status.unconfirmed", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.1.body" ] }, { - "title": "22nd Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.magic_staff.title", "level": 5, "elements": [ - "Not all spells work in the same way, but for those that do, a good staff is an essential tool for any beginner magi.", - "The simplest way to create these is to put a gem on the end of a stick. Yes, very revolutionary, I know." + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.2.body", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.3.body" ] }, { - "title": "Grogar's Bell", + "title": "item.unicopia.grogars_bell", "level": 0, "elements": [ { "item": { "item": "unicopia:grogars_bell" } }, - "Status: Lost", - "A powerful artifact once thought to be the source of King Grogar's power.", - "In skilled hooves, the bell may be used to transfer magical energy from one being to another." + "gui.unicopia.spellbook.chapter.artefacts.status.lost", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.1.body", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.2.body" ] }, { - "title": "22nd Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.title", "level": 80, "elements": [ - "It's theorised this artifact was forged from the claws of an Ursa Major during the era of Discord's reign, possibly as a weapon against the tyrant.", - "By its nature, the bell a corrupting influence inherent to it that will destroy the minds of anyone who dares weird its power." + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.3.body", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.4.body" ] }, { - "title": "Grogar's Bell II", + "title": "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.2.title", "level": 80, "elements": [ - "Legend says that after its first bearer, King Grogar, was driven to madness, the bell was stowed far away.", - "beyond most ponies' grasp, guarded inside an ancient city by an unbeatable beast." + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.5.body", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.6.body" ] }, { - "title": "Alicorn Amulet", + "title": "item.unicopia.alicorn_amulet", "level": 0, "elements": [ { "item": { "item": "unicopia:alicorn_amulet" } }, - "Status: Unconfirmed", - "Like the crystal heart, little is known of this artefact and thus nothing, not even its existance can be confirmed." + "gui.unicopia.spellbook.chapter.artefacts.status.unconfirmed", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.1.body" ] }, { - "title": "23nd Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.title", "level": 999, "elements": [ - "The alicorn amulet is a powerful force of dark magic created created by an unknown mage as their attempt to create the perfect being.", - "It combines the traits of all races into one powerful form, but it hard to control and addictive in nature. Anyone who uses it quickly becomes reliant on it and few attempt to remove it survive the ordeal." + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.2.body", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.3.body" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json index 1d888807..fb4e0968 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json @@ -5,54 +5,46 @@ "content": { "pages": [ { - "title": "Ch.5 The Arcane", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p1.title", "level": 10, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" } ] }, { - "title": "30th Hoof '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p2.title", "level": 8, "elements": [ - "A new communication from the crown today. The situation seems to be worsening in the west and though they've been satisfied with my work until now, they are putting pressure on me to produce something more destructive.", - "I tried to tell the messenger that I couldn't-" + "gui.unicopia.spellbook.chapter.dark_magic.p2.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p2.2.body" ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "3rd Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p3.title", "level": 9, "elements": [ - "Let it be known that it was never my intention that anypony use my work for nefarious purposes. I am a researcher, above and beyond all else. My intentions are pure, and my wants are nothing more than this world to be at peace." + "gui.unicopia.spellbook.chapter.dark_magic.p3.1.body" ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "4th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p4.title", "level": 10, "elements": [ - "No review of magic is ever complete without a glimpse into the other side.", - "Dark magic, or as I'm going to refer to it as The Arcane are tip-toeing the line between the normal light magic we normally know and the more nefarious side of reality.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p4.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p4.2.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Attraction", + "title": "spell.unicopia.vortex", "level": 5, "elements": [ - "If someone were to ask you 'what is the opposite of a repulsion spell, what would you say? An attraction spell, of course!", - "By twisting the purpose of the protection spell, I've been able to reverse its function to create a spell that pulls objects and entities closer to the caster.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/knowledge.png" } + "gui.unicopia.spellbook.chapter.dark_magic.vortex.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/knowledge.png" } ] }, { @@ -60,7 +52,7 @@ "level": 5, "elements": [ { "recipe": "unicopia:spells/vortex" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:shield" }, @@ -71,109 +63,95 @@ }, { "ingredients": [ - { "text": "+ 10x knowledge to narrow the effect's range to items" }, - { "text": "+ add focus trait to increase duration\n+ add power trait to increase range" } + { "text": "gui.unicopia.spellbook.chapter.dark_magic.vortex.modifier.1" }, + { "text": "gui.unicopia.spellbook.chapter.dark_magic.vortex.modifier.2" } ] } ] }, { - "title": "8th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p6.title", "level": 6, "elements": [ - "Additional Notes for the Attraction Spell", - "I caught Luna playing with my spellcrafting grid today, even though I expressly forbid her from entering my study when I'm not there.", - "Apparently it was over some dispute with Celly, I don't really remember, but it culminated in Luna sneaking into the study whilst I was out to get some bread." + "gui.unicopia.spellbook.chapter.dark_magic.p6.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p6.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p6.3.body" ] }, { - "title": "Arcane Attraction II", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p7.title", "level": 6, "elements": [ - "This isn't really about that, though. She's been scolded and sent back to her room, however as I was cleaning up the mess she'd made I noticed something in the piles of gems.", - "It's hard to describe, really. This is still distincly an attraction gem, but it's different.", - "It has traits I hadn't considered before, and the way it behaves... " + "gui.unicopia.spellbook.chapter.dark_magic.p7.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p7.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p7.3.body" ] }, { - "title": "Arcane Attraction II Cont.", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p8.title", "level": 6, "elements": [ - "Well I'll leave that up to tomorrow. I'm still tired from everything that's happened this week.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p8.1.body", + "gui.unicopia.spellbook.author1.name" ] }, { "title": "", "level": 6, "elements": [ - ">0 generosity --> ??", - ">20 order trait --> ???" + "gui.unicopia.spellbook.chapter.dark_magic.p8.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p8.3.body" ] }, { - "title": "20th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p9.title", "level": 11, "elements": [ - "As per their agreement, the council have sent certain...supplimental materials to aid in the new direction my research is taking. I was a little shocked at first.", - "This... thing... Whatever it is. Was, rather.", - "Is this really what we're fighting in the west?", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p9.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p9.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p9.3.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "21st Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p10.title", "level": 11, "elements": [ - "I've put the... thing. In the basement. Locked the door.", - "I just couldn't stand looking at it any longer. It's vaguely pony-shaped, but also...", - "I couldn't well let Luna see it. I've sent her out to spend the next few nights with Celly whilst I sort out what to do with this.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p10.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p10.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p10.3.body", + "gui.unicopia.spellbook.author1.name" ] }, + { }, + { }, { - "title": "", - "level": -1, - "elements": [] - }, - { - "title": "", - "level": -1, - "elements": [] - }, - { - "title": "25th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p11.title", "level": 13, "elements": [ - "I'm sorry for the long delays. Things have been... busy.", - "I've learned a lot about these creatures. Attached are some illustrations, done best I could so I wouldn't have to look at the thing directly.", - { - "text": "Its body is black and vaguely", - "extra": [ - { "text": "insect-like", "obfuscated": true }, - "with ponish proportions. It has no fur." - ] - } + "gui.unicopia.spellbook.chapter.dark_magic.p11.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p11.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p11.3.body" ] }, { "title": "", "level": 14, "elements": [ - "The magic they use is unusual. Not unusual, like what I've been studying. It's unnatural. Wild.", - "There is definitely something I might be able to harness here, but I shudder... Should I?", - "I fear that this may be a line that shouldn't be crossed.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p11.4.body", + "gui.unicopia.spellbook.chapter.dark_magic.p11.5.body", + "gui.unicopia.spellbook.chapter.dark_magic.p11.6.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Transmutation", + "title": "spell.unicopia.transformation", "level": 13, "elements": [ - "I've begun by simply harnessing their ability. It's unfocused and hard to control. I can rarely predict what is going to happen, but this gem has very clear transmodrification properties.", - "Throwing this at any creature has the chance to transform it into any other creature.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/life.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + "gui.unicopia.spellbook.chapter.dark_magic.transformation.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.transformation.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/life.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -181,7 +159,7 @@ "level": 13, "elements": [ { "recipe": "unicopia:spells/transformation" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 18, "trait": "unicopia:knowledge" }, @@ -192,13 +170,13 @@ ] }, { - "title": "Dispell Illusion", + "title": "spell.unicopia.reveal", "level": 15, "elements": [ - "Dispell Illusion is the first line of defense against transformation/illusion spells.", - "When cast it will force any nearby disguised changelings in its range to reveal their true form.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + "gui.unicopia.spellbook.chapter.dark_magic.reveal.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.reveal.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -206,7 +184,7 @@ "level": 15, "elements": [ { "recipe": "unicopia:spells/reveal" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:shield" }, @@ -215,66 +193,62 @@ { "count": 4, "trait": "unicopia:order" } ] }, - "* Increase range by adding the power trait" + "gui.unicopia.spellbook.chapter.dark_magic.reveal.modifier.1" ] }, { - "title": "27th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p14.title", "level": 14, "elements": [ - "Sleep the last few nights has become... elusive.", - "I don't know how to describe it, really. Luna appears unaffected, but every night after the sun goes down I find myself lying awake at night unable to sleep. It doesn't help that that sounds in the village have resumed." + "gui.unicopia.spellbook.chapter.dark_magic.p14.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p14.2.body" ] }, { - "title": "Lost Sleep", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p15.title", "level": 15, "elements": [ - "Last night, especially, I found myself pacing in the observatory. The air became chill, more than is normal for this time of year, and beyond anything that raging fire in corner of the room could combat.", - "The room where I keep the--my patient opposite the wall behind me." + "gui.unicopia.spellbook.chapter.dark_magic.p15.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p15.2.body" ] }, { - "title": "Lost Sleep Cont.", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p16.title", "level": 15, "elements": [ - "I've long since taken to keeping that door locked because every so often I could swear I heard something moving in there...", - "Gods, am I going crazy?", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p16.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p16.2.body", + "gui.unicopia.spellbook.author1.name" ] }, + { }, { - "title": "", - "level": -1, - "elements": [] - }, - { - "title": "29th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p17.title", "level": 18, "elements": [ - "Bits, there it is again!", - "I thought last night was a fluke, but I just heard it again--I'm shaking. My hooves, I can barely hold this book as I struggle to pen these words.", - "There's something--I heard something. Like a chittering--", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p17.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p17.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p17.3.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "1st Croptober '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p18.title", "level": 14, "elements": [ - "I made a call into town. One of the local blacksmiths have agreed to install a new lock, one of the heavy kind that not even earth ponies can bust.", - "I fear it may not be enough, though, it--whatever it is, clearly has magic. I may be forced to research a magical solution to my insomnia.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p18.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p18.2.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Arcane Protection", + "title": "spell.unicopia.arcane_protection", "level": 16, "elements": [ - "This spell will create a magical shroud that can be used to protect from other spellcasters.", - "No one else will be able to use magic within its radius.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } ] }, { @@ -282,7 +256,7 @@ "level": 16, "elements": [ { "recipe": "unicopia:spells/arcane_protection" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:shield" }, @@ -291,16 +265,16 @@ { "count": 1, "trait": "unicopia:darkness" } ] }, - "* Increase range by adding the power trait" + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.modifier.1" ] }, { - "title": "Displacement", + "title": "spell.unicopia.displacement", "level": 17, "elements": [ - "By casting this spell, a unicorn is able to swap their location with any other creature.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + "gui.unicopia.spellbook.chapter.dark_magic.displacement.1.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -308,7 +282,7 @@ "level": 17, "elements": [ { "recipe": "unicopia:spells/displacement" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "item": "unicopia:gemstone" }, @@ -319,28 +293,26 @@ ] }, { - "title": "3rd Croptober '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p21.title", "level": 14, "elements": [ - "The locks have been installed, and with the addition of some extra wardings, I'm feeling a little more at ease.", - "The motions at night have all but stopped, though I feel like I can almost hear it at times...", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p21.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p21.2.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "4th Cropt-", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p22.title", "level": 15, - "elements": [ - "" - ] + "elements": [] }, { - "title": "Mimic", + "title": "spell.unicopia.mimic", "level": 17, "elements": [ - "I've been able to tap into some of this strange creature's abilities. There's still a lot to figure out here, but for now I've merely distilled its essense into a gem.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } + "gui.unicopia.spellbook.chapter.dark_magic.mimic.1.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } ] }, { @@ -348,7 +320,7 @@ "level": 18, "elements": [ { "recipe": "unicopia:spells/mimic" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:transformation" }, @@ -357,67 +329,52 @@ { "count": 4, "trait": "unicopia:chaos" } ] }, - "* Add the focus trait to increase the effect's duration" + "gui.unicopia.spellbook.chapter.dark_magic.mimic.modifier.1" ] }, + { }, + { }, { - "title": "", - "level": 0, + "title": "??? ???? ??", + "level": 44, "elements": [ - "" - ] - }, - { - "title": "", - "level": 0, - "elements": [ - "" + "gui.unicopia.spellbook.chapter.dark_magic.p24.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p24.2.body" ] }, { "title": "??? ???? ??", "level": 44, "elements": [ - "There's been a wave of darkness that has come over the town. Nothing's been the same since that gods-forsaken creature arrived.", - "Ponies in town have begun remarking on lack of sleep, and it's showing. Just today I saw a poor stallion walking around in a daze. Bags under his eyes, barely any colour in his cheeks." + "gui.unicopia.spellbook.chapter.dark_magic.p25.1.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "??? ???? ??", - "level": 44, - "elements": [ - "He looked almost like a zombie the way he went through the motions, not really paying any attention even after he nearly ran into me.", - "- Starswirl the Bearded" - ] - }, - { - "title": "Scrap", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p26.title", "level": 21, "elements": [ - { - "text": "It's not enough. Never enough. Crawling. I feel crawling. Oh gods the crawling won't stop.", - "obfuscated": true - } + "gui.unicopia.spellbook.chapter.dark_magic.p26.1.body" ] }, { "title": "??? ???? ??", "level": 33, "elements": [ - "The insomnia. I can't take it. I lie in my bed every night waiting to go to sleep.", - "I thought I could stop it, keep it at bay, but I still hear it. That creature. Cold, unfeeling.", - "I feel myself being drained any time I'm around it. Is it... feeding on me?", - "No, it couldn't be." + "gui.unicopia.spellbook.chapter.dark_magic.p27.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p27.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p27.3.body", + "gui.unicopia.spellbook.chapter.dark_magic.p27.4.body" ] }, { "title": "????? ????", "level": 19, "elements": [ - "I found this incantation under some old notes whilst clearing out the lab. It's... simplistic and hard to manage, but it gets the job done.", - "- Starswirl the Bearded", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/kindness.png" } + "gui.unicopia.spellbook.chapter.dark_magic.p28.1.body", + "gui.unicopia.spellbook.author1.name", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/kindness.png" } ] }, { @@ -425,7 +382,7 @@ "level": 19, "elements": [ { "recipe": "unicopia:spells/dispel_evil" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:arcane_protection" }, @@ -434,7 +391,7 @@ { "count": 1, "trait": "unicopia:power" } ] }, - "* Add the power trait to increase the effect's range" + "gui.unicopia.spellbook.chapter.dark_magic.dispel_evil.modifier.1" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json index d7014015..491cb6de 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json @@ -5,30 +5,30 @@ "content": { "pages": [ { - "title": "Ch.2 Fire Magic", + "title": "gui.unicopia.spellbook.chapter.fire.p1.title", "level": 0, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/fire_magic.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/fire_magic.png" } ] }, { - "title": "9th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p2.title", "level": 0, "elements": [ - "It took me longer than I anticipated, nearly a month! Hah! But I present to you, dear reader, my findings for the first elementary form of magic: FIRE.", - "It's a working title, okay?", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p2.1.body", + "gui.unicopia.spellbook.chapter.fire.p2.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Scorch", + "title": "spell.unicopia.scorch", "level": 0, "elements": [ - "Simple and to the point, Scorch does exactly what you'd think. By embuing a gem with the fire trait, one can indute it to glow and become hot to the touch.", - "The effect becomes stronger the more fire you load it with, but take care not to overload it, as it may become volatile.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } + "gui.unicopia.spellbook.chapter.fire.scorch.1.body", + "gui.unicopia.spellbook.chapter.fire.scorch.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } ] }, { @@ -36,7 +36,7 @@ "level": 0, "elements": [ { "recipe": "unicopia:spells/scorch" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 10, "trait": "unicopia:fire" } @@ -45,13 +45,13 @@ ] }, { - "title": "Flame", + "title": "spell.unicopia.flame", "level": 1, "elements": [ - "Creates a heating affect up to a radius of 3 hooves from any surfaces it touches.", - "Useful when one needs a flame in a hurry or to fend off a wild wendigo.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } + "gui.unicopia.spellbook.chapter.fire.flame.1.body", + "gui.unicopia.spellbook.chapter.fire.flame.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } ] }, { @@ -59,7 +59,7 @@ "level": 1, "elements": [ { "recipe": "unicopia:spells/flame" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "item": "unicopia:gemstone" }, @@ -69,62 +69,53 @@ } ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "10th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p5.title", "level": 0, "elements": [ - "Progress?", - "I've managed to improve the previous spell somewhat, but there is still something lacking. It's all very orderly. Predicatable.", - "Luna has suggested adding more fire, but I'm weary to create more scorch marks on the tower. Faust save me if anypony were to find out what I've been doing..." + "gui.unicopia.spellbook.chapter.fire.p5.1.body", + "gui.unicopia.spellbook.chapter.fire.p5.2.body", + "gui.unicopia.spellbook.chapter.fire.p5.3.body" ] }, { - "title": "Fire Magic III", + "title": "gui.unicopia.spellbook.chapter.fire.p6.title", "level": 2, "elements": [ - "Focusing Magic", - "Some spells normally take a great amount of focus to cast, and a steady wit to control, However I've found objects embued with the focusing trait work wonderfully as a substitute for when the caster is lacking.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } + "gui.unicopia.spellbook.chapter.fire.p6.1.body", + "gui.unicopia.spellbook.chapter.fire.p6.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } ] }, { - "title": "Fire Magic III-II", + "title": "gui.unicopia.spellbook.chapter.fire.p7.title", "level": 2, "elements": [ - "Any glass objects you can find, eyes, bottles, whatever incorporates a lense can be used to embue focus on a spell.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p7.1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Scrap 2", + "title": "gui.unicopia.spellbook.chapter.fire.p8.title", "level": 21, "elements": [ - "We went to the market today. Had to get out of that tower, do something, be somewhere. Luna suggested we go in to see what they were selling for the fair so I decided to indulge her.", - "The townsfolk are still rather skeptical of us, though who's to blame them. Luna was getting along well with the other foals, at least." + "gui.unicopia.spellbook.chapter.fire.p8.1.body", + "gui.unicopia.spellbook.chapter.fire.p8.2.body" ] }, + { }, { - "title": "", - "level": 21, - "elements": [ - ] - }, - { - "title": "Fire Bolt", + "title": "spell.unicopia.fire_bolt", "level": 2, "elements": [ - "Creates a series of heated projectiles to fling at a target. Upon impact the target will be set alight.", - "- Increase focus will allow finer grained control of the projectile's trajectory.", - "- With over 50 focus it's almost like they know where the target is (homing?).", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } + "gui.unicopia.spellbook.chapter.fire.fire_bolt.1.body", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.2.body", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.3.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } ] }, { @@ -132,7 +123,7 @@ "level": 2, "elements": [ { "recipe": "unicopia:spells/fire_bolt" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:flame" }, @@ -143,105 +134,81 @@ ] }, { - "title": "Fire Magic IV", + "title": "gui.unicopia.spellbook.chapter.fire.p10.title", "level": 2, "elements": [ - "Powerful Magic", - "Where some spells take focus, others require power. Either to exert a force, or to generate energy in some form.", - "There are few unicorns that have the inherent strength and power to cast spells of these kinds, but luckily such a trait is not in short supply around us.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/strength.png" } + "gui.unicopia.spellbook.chapter.fire.p10.1.body", + "gui.unicopia.spellbook.chapter.fire.p10.2.body", + "gui.unicopia.spellbook.chapter.fire.p10.3.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/strength.png" } ] }, { - "title": "Fire Magic IV-II", + "title": "gui.unicopia.spellbook.chapter.fire.p11.title", "level": 2, "elements": [ - "Earthly elements, stone, many metals and minerals, that are strong under compression will exhibit the strength trait.", - "Electrical/Conductive elements that can be used to power things, or that glow can also be used to obtain the power trait.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p11.1.body", + "gui.unicopia.spellbook.chapter.fire.p11.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "11th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p12.title", "level": 2, "elements": [ - "Got a knock at the door today. Luna was very eager to answer it but I had to send her away as it was a messenger from the crown.", - "My research has garnered a certain bit of notoriety, it appears. The royals are very eager to see what I have concocted.", - "They've gotten it in their heads that they can use it against the West." + "gui.unicopia.spellbook.chapter.fire.p12.1.body", + "gui.unicopia.spellbook.chapter.fire.p12.2.body", + "gui.unicopia.spellbook.chapter.fire.p12.3.body" ] }, { "title": "", "level": 13, "elements": [ - "Gods forbid they succeed. I shudder to think what the council might do if they got their hooves on my work.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p12.4.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { "title": "", "level": 3, "elements": [ - "Addendum", - { - "text": "", - "strikethrough": true, - "extra": [ - {"text": "I am told the crown has started giving directions to find other uses. Ways to..."} - ] - } + "gui.unicopia.spellbook.chapter.fire.p12.5.body", + "gui.unicopia.spellbook.chapter.fire.p12.6.body" ] }, + { }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "20th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p13.title", "level": 9, "elements": [ - "I have word from the crown. They appear satisfied, for now, and have agreed to let my continue my research as I have into the winter.", - "I am under duress to destroy the last several entries, I'm afraid.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p13.1.body", + "gui.unicopia.spellbook.chapter.fire.p13.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "21st Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p14.title", "level": 0, "elements": [ - { - "text": "I shall visit", - "extra": [ - { "text": "Commander Hurricane", "obfuscated": true }, - " tomorrow. Perhaps she may shed light onto my predicament." - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p14.1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "Protection", + "title": "spell.unicopia.shield", "level": 2, "elements": [ - "Casting shields are one of the first things every unicorn learns in self-defense. It's simple and easy to cast, and is an excellent introduction to incanting.", - "Its disadvantage is the energy and mental cost, however we can negate both by attaching it to a gem as per follows...", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" } + "gui.unicopia.spellbook.chapter.fire.shield.1.body", + "gui.unicopia.spellbook.chapter.fire.shield.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" } ] }, { @@ -249,7 +216,7 @@ "level": 2, "elements": [ { "recipe": "unicopia:spells/shield" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "item": "unicopia:gemstone" }, @@ -258,35 +225,30 @@ { "count": 10, "trait": "unicopia:power" } ] }, - "+ add power trait to increase effect range" + "gui.unicopia.spellbook.chapter.fire.shield.modifier.1" ] }, { - "title": "Protection II", + "title": "gui.unicopia.spellbook.chapter.fire.p16.title", "level": 4, "elements": [ - "By adding extra traits, I was able to slightly modify the shield to allow or deny certain parties into the effect range.", - "+ add life trait --> all animals may enter\n+ add blood trait --> all monsters may enter\n+ add ice trait --> all ponies may enter" + "gui.unicopia.spellbook.chapter.fire.p16.1.body", + "gui.unicopia.spellbook.chapter.fire.p16.2.body" ] }, { - "title": "Protection III", + "title": "gui.unicopia.spellbook.chapter.fire.p17.title", "level": 5, "elements": [ - "+ add genorosity trait to attach the spell to a location rather than yourself" + "gui.unicopia.spellbook.chapter.fire.p17.1.body" ] }, { - "title": "Scrap: 9th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p18.title", "level": 3, "elements": [ - "Fire magic has proven to a be a little more... unpredictable than anticipated. Every time I feel like I'm making progress it finds a way to set me back.", - { - "text": "I can't stop now, though...I'm told the situation in the west is growing dire. They have asked me to pick up the pace and produce something we can use to get the upper hoof against the", - "extra": [ - { "text": "Changeling Storm.", "obfuscated": true} - ] - } + "gui.unicopia.spellbook.chapter.fire.p18.1.body", + "gui.unicopia.spellbook.chapter.fire.p18.2.body" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json index 1c05cb6b..fcf6fa57 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json @@ -5,30 +5,30 @@ "content": { "pages": [ { - "title": "Ch.3 Ice Magic", + "title": "gui.unicopia.spellbook.chapter.ice.p1.title", "level": 0, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/ice_magic.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/ice_magic.png" } ] }, { - "title": "4th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p2.title", "level": 0, "elements": [ - "This is an interesting one. Rather simple, I admit, but Luna insisted I make something cold to help us deal with this darn heat.", - "All you need is a gem and something cold. Like a snowball.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p2.1.body", + "gui.unicopia.spellbook.chapter.ice.p2.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Frost", + "title": "spell.unicopia.frost", "level": 0, "elements": [ - "Creates a chilling affect up to a radius of 3 hooves from any surfaces it touches.", - "Will sap energy out of the immediate environment causing a phase change.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + "gui.unicopia.spellbook.chapter.ice.frost.1.body", + "gui.unicopia.spellbook.chapter.ice.frost.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { @@ -36,7 +36,7 @@ "level": 3, "elements": [ { "recipe": "unicopia:spells/frost" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 15, "trait": "unicopia:ice" } @@ -45,13 +45,13 @@ ] }, { - "title": "Chilling Breath", + "title": "gui.unicopia.spellbook.chapter.ice.p4.title", "level": 0, "elements": [ - "Alters the ability of certain objects to distenguish between hot and cold.", - "This is a very weak spell, but when used with a boat can be exceedingly useful to get out of a sticky stituation.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/oak_boat.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + "gui.unicopia.spellbook.chapter.ice.p4.1.body", + "gui.unicopia.spellbook.chapter.ice.p4.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "minecraft:textures/item/oak_boat.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { @@ -59,7 +59,7 @@ "level": 0, "elements": [ { "recipe": "unicopia:spells/chilling_breath" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:frost" }, @@ -70,71 +70,67 @@ ] }, { - "title": "5th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p5.title", "level": 4, "elements": [ - "The village-ponies had a bonfire last night. I could tell by the strong smell of burning wood and the sound of music.", - "Luna, bless her heart, insisted that we take a break to join them. She had to practically drag me away from my desk to do it.", - "What can I say? She's a light in my heart." + "gui.unicopia.spellbook.chapter.ice.p5.1.body", + "gui.unicopia.spellbook.chapter.ice.p5.2.body", + "gui.unicopia.spellbook.chapter.ice.p5.3.body" ] }, { - "title": "Bonfire", + "title": "gui.unicopia.spellbook.chapter.ice.p6.title", "level": 5, "elements": [ - "We arrived at the bonfire, and of course the first thing they had was a mug of ale in my hoof. I didn't drink it, of course-alcohol is a poison to me. I'd be sick as a mule.", - "Luna enjoyed it-the bonfire, not the ale!-though. She made immediate friends with one of the town's fillies, Celly or something. They played the whole night." + "gui.unicopia.spellbook.chapter.ice.p6.1.body", + "gui.unicopia.spellbook.chapter.ice.p6.2.body" ] }, { - "title": "Bonfire II", + "title": "gui.unicopia.spellbook.chapter.ice.p7.title", "level": 6, "elements": [ - "On the way back Luna was telling me of the stories her friend told her. The town has a lot of legends, as to be expected.", - "One of them was about a scary old warlock who lived in a haunted tower at the edge of town. There's no mystery who that was about.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p7.1.body", + "gui.unicopia.spellbook.chapter.ice.p7.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, - { - "title": "", - "level": -1, - "elements": [] - }, + { }, { "title": "", "level": 6, "elements": [ - "Ice Spell II", - "Creates a cooling affect up to a radius of 3 hooves from any surfaces it touches.", - "Requires:", + "gui.unicopia.spellbook.chapter.ice.p7.3.body", + "gui.unicopia.spellbook.chapter.ice.p7.4.body", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 15, "trait": "unicopia:ice" } ] }, - { "x": 115, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" }, - { "x": 115, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + { "x": 115, "y": -10, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" }, + { "x": 115, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { - "title": "6th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p8.title", "level": 4, "elements": [ - "There was a strange noise in the village last night. Very strange. I heard a lot ponies shouting and there may have been a fire.", - "I hope everything is okay.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p8.1.body", + "gui.unicopia.spellbook.chapter.ice.p8.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Dancing Lights", + "title": "spell.unicopia.light", "level": 5, "elements": [ - "By combining a fire bolt gem with a splash of life and the chilling effect of ice I've created a spell to help with seeing in the night.", - "Dancing Lights will summon a cluster of glowing orbs to illuminate your path.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/light.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + "gui.unicopia.spellbook.chapter.ice.light.1.body", + "gui.unicopia.spellbook.chapter.ice.light.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "minecraft:textures/item/light.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { @@ -142,7 +138,7 @@ "level": 5, "elements": [ { "recipe": "unicopia:spells/light" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:fire_bolt" }, @@ -151,140 +147,135 @@ { "count": 30, "trait": "unicopia:ice" } ] }, - "* By adding more focus you can extend the duration of the spell" + "gui.unicopia.spellbook.chapter.ice.light.modifier.1" ] }, { - "title": "12th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p10.title", "level": 4, "elements": [ - "There were more noises last night, this time a lot closer. The town's dimeaner has also changed. A lot of the ponies I see that are normally very cheerful have become sullen.", - "Something has happened, that much is obvious, though few will tell me what.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p10.1.body", + "gui.unicopia.spellbook.chapter.ice.p10.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "15th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p11.title", "level": 4, "elements": [ - "Winter is nearly upon us now. I just saw the earliest flakes of snow outside this window as I write.", - { - "text": "The locals have begun their winter unwrapping and though the usual grumblings about frozen fields abound, I can tell there is still ", - "extra": [ - { "text": "a sense of uneasyness.", "obfuscated": true} - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p11.1.body", + "gui.unicopia.spellbook.chapter.ice.p11.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "17th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p12.title", "level": 6, "elements": [ - "It's rather surprising how quickly the weather starts to change around here. Everything in Catermoore is so very well controlled, with the spells we use to manage temperature and the pegasi assisting with the weather, we sometimes forget what wild seasons can be like.", - "But these Earth Ponies don't ave any of those luxuries. They have to deal with the weather as it comes." + "gui.unicopia.spellbook.chapter.ice.p12.1.body", + "gui.unicopia.spellbook.chapter.ice.p12.2.body" ] }, { - "title": "Frozen Lake", + "title": "gui.unicopia.spellbook.chapter.ice.p13.title", "level": 6, "elements": [ - "Just this morning I was on the way to the stall when I passed the lake at the foot of this tower's hill and saw it was nearly completely frozen over. A few colts had taken to scating across it.", - "Luna asked if she could join them, but I didn't allow it. I couldn't say why at the time, but I had a bad feeling, like something was going to happen..." + "gui.unicopia.spellbook.chapter.ice.p13.1.body", + "gui.unicopia.spellbook.chapter.ice.p13.2.body" ] }, { - "title": "17th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p14.title", "level": 19, "elements": [ - "Oh gods, of princesses. I. I don't know how to describe this. My hooves are shaking, I can barely breeze. I'm freezing cold and I can't stop thinking about what happened.", - "I knew there was something wrong. I b---knew it. I didn't know what it was at the time, but I'm extremely glad I didn't let Luna scate on that lake." + "gui.unicopia.spellbook.chapter.ice.p14.1.body", + "gui.unicopia.spellbook.chapter.ice.p14.2.body" ] }, { - "title": "Frozen Lake II", + "title": "gui.unicopia.spellbook.chapter.ice.p15.title", "level": 19, "elements": [ - "Let me take a step back a bit to describe what happened. Do you remember the frozen lake and those colts that were skating on it?", - "Well, after that I went on to the market and I was looking at the strawberries in one of the stalls--Didn't get any, sadly they were forgotten in the confusion." + "gui.unicopia.spellbook.chapter.ice.p15.1.body", + "gui.unicopia.spellbook.chapter.ice.p15.2.body" ] }, { - "title": "Frozen Lake III", + "title": "gui.unicopia.spellbook.chapter.ice.p16.title", "level": 19, "elements": [ - "Whilst I was debating the vendor about the price, I felt Luna tugging on my tunic and she asked me what was going on. I heard galloping and I saw ponies running by us. None of them stopped to explain, but I heard some very gruff words in old ponish.", - "They were heading in the direction of our tower so I set off after them." + "gui.unicopia.spellbook.chapter.ice.p16.1.body", + "gui.unicopia.spellbook.chapter.ice.p16.2.body" ] }, { "title": "", "level": 20, "elements": [ - "My immediate thoughts were 'was it a fire'? Was my lab in danger?", - "I tell you what, I wish that were the case. What I actually found was much, much, worse, and even thinking of it makes my blood run cold anew." + "gui.unicopia.spellbook.chapter.ice.p16.3.body", + "gui.unicopia.spellbook.chapter.ice.p16.4.body" ] }, { - "title": "Frozen Lake IV", + "title": "gui.unicopia.spellbook.chapter.ice.p17.title", "level": 20, "elements": [ - "When we were getting near the lake from earlier, I saw a large crowd growing along its banks. Ladders and emergency equipment were out and scattered on the shoreline and a loud uproar had erupted about what to do.", - "Getting closer, though, I realised what had happened soon enough--and I made a beeline for for the water-- The ice was broken and the lake was freezing cold." + "gui.unicopia.spellbook.chapter.ice.p17.1.body", + "gui.unicopia.spellbook.chapter.ice.p17.2.body" ] }, { "title": "", "level": 20, "elements": [ - "I jumped in anyway, pulling my saddlebags open with my magic, and grabbed the last of the gems i had with my and cast the unfinished spell it had inside.", - "The waters receded away from me as I galloped down the slop and across the drying lake bed and dove to catch the colts that had fallen in." + "gui.unicopia.spellbook.chapter.ice.p17.3.body", + "gui.unicopia.spellbook.chapter.ice.p17.4.body" ] }, { - "title": "Frozen Lake V", + "title": "gui.unicopia.spellbook.chapter.ice.p18.title", "level": 20, "elements": [ - "They were freezing and wet, even as the spell's effects worked to pull the water away from their coats, we carred them up to the shore and got them covered in blankets with hot drinks in their hooves.", - "The townponies insisted on giving me a blanket of my own, even though I hadn't - couldn't have- gotten wet." + "gui.unicopia.spellbook.chapter.ice.p18.1.body", + "gui.unicopia.spellbook.chapter.ice.p18.2.body" ] }, { "title": "", "level": 20, "elements": [ - "It was only much later, when the shock began to wear of and I was feeling my head start to pound that I remembered to cancel the spell.", - "Thank the princesses we got there in time.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p18.3.body", + "gui.unicopia.spellbook.chapter.ice.p18.4.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "18th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p19.title", "level": 7, "elements": [ - "No sign of the colts this morning, I assume they won't be coming near this lake for a long while. The water had frozen again in the night and looked peaceful.", - "The unseasy feeling I had yesterday was gone today so I was able to relax on its banks with Luna. She didn't want to swim in this lake any more, and I don't blame her. I wouldn't either." + "gui.unicopia.spellbook.chapter.ice.p19.1.body", + "gui.unicopia.spellbook.chapter.ice.p19.2.body" ] }, { - "title": "Sandcastle", + "title": "gui.unicopia.spellbook.chapter.ice.p20.title", "level": 7, "elements": [ - "Luna started a sand castle, and whilst she was busy with that I decided to sketch out the details of my new spell.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p20.1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Water Repulsion", + "title": "spell.unicopia.hydrophobic", "level": 20, "elements": [ - "By combining the abilities of a shield spell with that of the frost gem, the results are... Admittedly strange.", - "I'm calling this water repulsion because it does just that: It pushes water away from the caster.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + "gui.unicopia.spellbook.chapter.ice.hydrophobic.1.body", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { @@ -292,7 +283,7 @@ "level": 20, "elements": [ { "recipe": "unicopia:spells/hydrophobic" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:frost" }, @@ -302,8 +293,8 @@ }, { "ingredients": [ - { "text": "* By adding more focus you can extend the duration of the spell" }, - { "text": "* Add the generosity trait to tie this spell to a location rather than a user" } + { "text": "gui.unicopia.spellbook.chapter.ice.hydrophobic.modifier.1" }, + { "text": "gui.unicopia.spellbook.chapter.ice.hydrophobic.modifier.2" } ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/introduction.json b/src/main/resources/data/unicopia/spellbook/chapters/introduction.json index d34b424d..b2cca4f9 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/introduction.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/introduction.json @@ -5,112 +5,88 @@ "content": { "pages": [ { - "title": "Preface", + "title": "gui.unicopia.spellbook.chapter.introduction.p1.title", "level": 0, "elements": [ - { - "text": "To whomever holds this tome, beware what you seek for you might not like what you find.", - "extra": [ - { "text": "Hither yonder equs.", "obfuscated": true } - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.introduction.p1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Ch.1 Magic in Equestria", + "title": "gui.unicopia.spellbook.chapter.introduction.p2.title", "level": 0, "elements": [ - { - "text": "Equestria is filled with magic of all different shapes and forms. Following recent events, however, it's has become plainly obvious that we do not fully understand all that there is about the world of Equestria. That is why the crown has tasked me with researching Magic in all of its forms, so we might utilise it and, I hope, save ourselves from the", - "extra": [ - { "text": "aaaaaaa", "obfuscated": "true" } - ] - }, + "gui.unicopia.spellbook.chapter.introduction.p2.body", { "x": 125, "y": 0, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/profile.png" } ] }, { - "title": "1st Mare '12", + "title": "gui.unicopia.spellbook.chapter.introduction.p3.title", "level": 0, "elements": [ - "Odd Rocks", - "These 'Gemstones' as the locals call them are a common material found around the world. Farm-Ponies dig them up all the time and consider it a local delicacy, but I believe these stones are capable of a lot more than they let on.", + "gui.unicopia.spellbook.chapter.introduction.p3.1.body", + "gui.unicopia.spellbook.chapter.introduction.p3.2.body", { "x": 125, "y": 0, "width": 32, "height": 32, "texture": "unicopia:textures/item/gemstone.png" } ] }, { - "title": "Gemstones", + "title": "gui.unicopia.spellbook.chapter.introduction.p4.title", "level": 0, "elements": [ - { - "text": "My research is still incomplete but I may have stumbled upon something. These stones have high magical potentia! More than I've ever seen before!" - }, - { - "text": "Luna-wants", - "strikethrough": true, - "extra": [ - { "text": "I'm going to keep experimenting. Hooves-crossed, I'll update you tomorrow if I find anything.", "strikethrough": false} - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.introduction.p4.1.body", + "gui.unicopia.spellbook.chapter.introduction.p4.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "2nd Mare '12", + "title": "gui.unicopia.spellbook.chapter.introduction.p5.title", "level": 0, "elements": [ - { - "text": "It worked! Holy", - "extra": [ - { "text": "Celestia's ass-cheeks'", "obfuscated": true}, - { "text": "it actually worked." } - ] - }, - "This is amazing! These can do so much more than I could have ever imagined. Think of the advances I could bring to Equestria. Gem-powered lighting, heating, cooling, I'd no longer have to spend summer sitting on this-", - "I'm getting ahead of myself. Let me explain..." + "gui.unicopia.spellbook.chapter.introduction.p5.1.body", + "gui.unicopia.spellbook.chapter.introduction.p5.2.body", + "gui.unicopia.spellbook.chapter.introduction.p5.3.body" ] }, { - "title": "Spellcrafting", + "title": "gui.unicopia.spellbook.chapter.introduction.p6.title", "level": 0, "elements": [ { "x": 125, "y": 0, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/crafting.png" }, - "I drew a guide at the start of this book to help with the placement.", - "Put a raw gem-it mustn't have any spells already-in the middle and place materials around it in the slots I marked.", - "Each material gives different effects and putting them closer enhances their influence on the gem." + "gui.unicopia.spellbook.chapter.introduction.p6.1.body", + "gui.unicopia.spellbook.chapter.introduction.p6.2.body", + "gui.unicopia.spellbook.chapter.introduction.p6.3.body" ] }, { - "title": "3rd Mare '12", + "title": "gui.unicopia.spellbook.chapter.introduction.p7.title", "level": 0, "elements": [ - "I'm going to start documenting spell combinations as I find them. Some of them are pretty obvious, like gem + fire = fire gem", - "But some are less clear. For instance, what traits would an egg add? Much experimenting is needed. Oh, I'm giddy with excitement!", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.introduction.p7.1.body", + "gui.unicopia.spellbook.chapter.introduction.p7.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Botched Gems", + "title": "gui.unicopia.spellbook.chapter.introduction.p8.title", "level": 1, "elements": [ - { "x": 127, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/item/botched_gem.png" }, - "Not every combination works. What's dissapointing is now I have all these useless stones piling up in my chambers.", - "I don't know what to do with them. They're not edible. At least the locals don't think so.", - "They do still have the traits I gave them, so maybe I can find a use other than building a rock-fort with little Luna..." + { "x": 127, "y": 0, "width": 32, "height": 32, "texture": "unicopia:textures/item/botched_gem.png" }, + "gui.unicopia.spellbook.chapter.introduction.p8.1.body", + "gui.unicopia.spellbook.chapter.introduction.p8.2.body", + "gui.unicopia.spellbook.chapter.introduction.p8.3.body" ] }, { - "title": "13th Mare '12", + "title": "gui.unicopia.spellbook.chapter.introduction.p9.title", "level": 0, "elements": [ - "Sorry for the long delay in updates. I've been hard at work researching different spells and desciding my approach.", - "Fire is becomg a very interesting aspect, what with traits for it being readily available.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.introduction.p9.1.body", + "gui.unicopia.spellbook.chapter.introduction.p9.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json b/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json index aee0fb23..57a28e5c 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json @@ -5,28 +5,28 @@ "content": { "pages": [ { - "title": "Ch.6 The Beyond", + "title": "gui.unicopia.spellbook.chapter.otherworldly.p1.title", "level": 10, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/the_otherworldly.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/the_otherworldly.png" } ] }, { - "title": "2nd Croptober '12", + "title": "gui.unicopia.spellbook.chapter.otherworldly.p2.title", "level": 20, "elements": [ - "This chapter serves as an exploration of the worlds beyond our own and a delving into what most unicorns would normally shy away from.", - "In this section can be found the most powerful of the powerful spells, but also the most danger. Read further at your own peril, as this is not for the light of mind.", - "- Lord Sombra" + "gui.unicopia.spellbook.chapter.otherworldly.p2.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.p2.2.body", + "gui.unicopia.spellbook.author2.name" ] }, { - "title": "Life Sapping", + "title": "spell.unicopia.siphoning", "level": 20, "elements": [ - "A simple spell that siphons life force out of a living entity and uses it to revitalise the caster.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } + "gui.unicopia.spellbook.chapter.otherworldly.siphoning.1.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } ] }, { @@ -34,7 +34,7 @@ "level": 20, "elements": [ { "recipe": "unicopia:spells/siphoning" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:infernal" }, @@ -45,18 +45,13 @@ ] }, { - "title": "Necromancy", + "title": "spell.unicopia.necromancy", "level": 21, "elements": [ - "This area effect spell taps into the great beyond to summon life to serve its master.", - { - "text": "This spell is not very useful when used on its own, but combined with other traits may become a powerful tool against the", - "extra": [ - { "text": "Changeling Swarms.", "obfuscated": true } - ] - }, - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } + "gui.unicopia.spellbook.chapter.otherworldly.necromancy.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.necromancy.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } ] }, { @@ -64,7 +59,7 @@ "level": 21, "elements": [ { "recipe": "unicopia:spells/necromancy" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:siphoning" }, @@ -79,13 +74,13 @@ ] }, { - "title": "Dark Vortex", + "title": "spell.unicopia.dark_vortex", "level": 23, "elements": [ - "Dipping below the fabric of reality, this spell taps into the deep arcane powers of the beyond to punch a hole in reality.", - "The resulting hole is a hungry mass that consumes all who approach. It grants massive energy, but feed it at your peril.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/chaos.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } + "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/chaos.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } ] }, { @@ -93,7 +88,7 @@ "level": 23, "elements": [ { "recipe": "unicopia:spells/dark_vortex" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:vortex" }, @@ -106,13 +101,13 @@ ] }, { - "title": "Arcane Rift", + "title": "spell.unicopia.portal", "level": 24, "elements": [ - "Combining the effects of the displacement spell created by my predecessor and the dark vortex gem, one is able to tame its chaotic nature.", - "The arcane rift spell allows the caster to link two locations together to create a bridge across the ether. Anything that enters one end will appear at the other maintaining its velocity.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + "gui.unicopia.spellbook.chapter.otherworldly.portal.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.portal.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -120,7 +115,7 @@ "level": 24, "elements": [ { "recipe": "unicopia:spells/portal" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "item": "unicopia:gemstone" }, @@ -133,13 +128,13 @@ ] }, { - "title": "Mind Swap", + "title": "spell.unicopia.mind_swap", "level": 27, "elements": [ - "Continuing my predecessor's research into the abilities of the Changeling Spawn, I have enhanced his mimic spell by adding a chaotic twist.", - "Mind Swap extends the effects of mimic to cover two individuals, in effect swapping their bodies for a limited time.", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.2.body", + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -147,7 +142,7 @@ "level": 28, "elements": [ { "recipe": "unicopia:spells/mind_swap" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:mimic" }, @@ -156,7 +151,7 @@ { "count": 40, "trait": "unicopia:chaos" } ] }, - "* Add the focus trait to increase the effect's duration" + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.3.body" ] } ] diff --git a/src/main/resources/data/unicopia/structures/changeling_hive/pit_decoration/bulb.nbt b/src/main/resources/data/unicopia/structures/changeling_hive/pit_decoration/bulb.nbt new file mode 100644 index 00000000..daf0e314 Binary files /dev/null and b/src/main/resources/data/unicopia/structures/changeling_hive/pit_decoration/bulb.nbt differ diff --git a/src/main/resources/data/unicopia/structures/changeling_hive/trapped/offshoot2.nbt b/src/main/resources/data/unicopia/structures/changeling_hive/trapped/offshoot2.nbt new file mode 100644 index 00000000..497a7b7d Binary files /dev/null and b/src/main/resources/data/unicopia/structures/changeling_hive/trapped/offshoot2.nbt differ diff --git a/src/main/resources/data/unicopia/tags/blocks/kicks_up_dust.json b/src/main/resources/data/unicopia/tags/blocks/kicks_up_dust.json new file mode 100644 index 00000000..5a3a6783 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/blocks/kicks_up_dust.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "#minecraft:sand", + "minecraft:gravel", + "minecraft:suspicious_gravel", + "#c:concrete_powders" + ] +} diff --git a/src/main/resources/data/unicopia/tags/blocks/palm_logs.json b/src/main/resources/data/unicopia/tags/blocks/palm_logs.json new file mode 100644 index 00000000..38397972 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/blocks/palm_logs.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_log", + "unicopia:palm_wood", + "unicopia:stripped_palm_log", + "unicopia:stripped_palm_wood" + ] +} diff --git a/src/main/resources/data/unicopia/tags/blocks/unaffected_by_grow_ability.json b/src/main/resources/data/unicopia/tags/blocks/unaffected_by_grow_ability.json new file mode 100644 index 00000000..06c918c6 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/blocks/unaffected_by_grow_ability.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:grass_block" + ] +} diff --git a/src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json b/src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json new file mode 100644 index 00000000..276d2073 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:horseshoe" + ] +} diff --git a/src/main/resources/data/unicopia/tags/items/badges.json b/src/main/resources/data/unicopia/tags/items/badges.json new file mode 100644 index 00000000..16481fc4 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/items/badges.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "unicopia:earth_badge", + "unicopia:unicorn_badge", + "unicopia:pegasus_badge", + "unicopia:changeling_badge", + "unicopia:bat_badge", + "unicopia:kirin_badge", + "unicopia:alicorn_badge", + "unicopia:hippogriff_badge" + ] +} diff --git a/src/main/resources/data/unicopia/tags/items/chitin.json b/src/main/resources/data/unicopia/tags/items/chitin.json new file mode 100644 index 00000000..aff5bfad --- /dev/null +++ b/src/main/resources/data/unicopia/tags/items/chitin.json @@ -0,0 +1,12 @@ +{ + "replace": false, + "values": [ + "unicopia:chitin", + "unicopia:surface_chitin", + "unicopia:chiselled_chitin", + "unicopia:chiselled_chitin_slab", + "unicopia:chiselled_chitin_stairs", + "unicopia:chiselled_chitin_hull", + "unicopia:chitin_spikes" + ] +} diff --git a/src/main/resources/data/unicopia/tags/items/cloud_slabs.json b/src/main/resources/data/unicopia/tags/items/cloud_slabs.json index a7337a0e..d17c86ae 100644 --- a/src/main/resources/data/unicopia/tags/items/cloud_slabs.json +++ b/src/main/resources/data/unicopia/tags/items/cloud_slabs.json @@ -3,6 +3,7 @@ "values": [ "unicopia:cloud_slab", "unicopia:dense_cloud_slab", + "unicopia:cloud_brick_slab", "unicopia:etched_cloud_slab", "unicopia:cloud_plank_slab" ] diff --git a/src/main/resources/data/unicopia/tags/items/cloud_stairs.json b/src/main/resources/data/unicopia/tags/items/cloud_stairs.json index cbbba0dc..49b0d346 100644 --- a/src/main/resources/data/unicopia/tags/items/cloud_stairs.json +++ b/src/main/resources/data/unicopia/tags/items/cloud_stairs.json @@ -3,7 +3,9 @@ "values": [ "unicopia:cloud_stairs", "unicopia:dense_cloud_stairs", + "unicopia:cloud_brick_stairs", "unicopia:etched_cloud_stairs", - "unicopia:cloud_plank_stairs" + "unicopia:cloud_plank_stairs", + "unicopia:cloud_chest" ] } diff --git a/src/main/resources/data/unicopia/tags/items/falls_slowly.json b/src/main/resources/data/unicopia/tags/items/falls_slowly.json index d413b877..49ac7cee 100644 --- a/src/main/resources/data/unicopia/tags/items/falls_slowly.json +++ b/src/main/resources/data/unicopia/tags/items/falls_slowly.json @@ -2,6 +2,7 @@ "replace": false, "values": [ "minecraft:feather", - "#unicopia:magic_feathers" + "#unicopia:magic_feathers", + "unicopia:cloud_lump" ] } diff --git a/src/main/resources/data/unicopia/tags/items/floats_on_clouds.json b/src/main/resources/data/unicopia/tags/items/floats_on_clouds.json index 189ac69b..cc18beba 100644 --- a/src/main/resources/data/unicopia/tags/items/floats_on_clouds.json +++ b/src/main/resources/data/unicopia/tags/items/floats_on_clouds.json @@ -5,6 +5,8 @@ "#unicopia:cloud_slabs", "#unicopia:cloud_stairs", "#unicopia:cloud_beds", - "unicopia:cloud_pillar" + "unicopia:cloud_pillar", + "unicopia:carved_cloud", + "unicopia:shaping_bench" ] } diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json index ce1ec0f6..5bf8d28d 100644 --- a/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json +++ b/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json @@ -3,7 +3,6 @@ "values": [ "minecraft:hay_block", "#minecraft:leaves", - "#c:foraging/edibles_filling", - { "id": "farmersdelight:rice_bale", "required": false } + "#c:foraging/edibles_filling" ] } diff --git a/src/main/resources/data/unicopia/tags/items/has_no_traits.json b/src/main/resources/data/unicopia/tags/items/has_no_traits.json new file mode 100644 index 00000000..1a300515 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/items/has_no_traits.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:air", + "minecraft:spawner", + "minecraft:structure_void", + "minecraft:structure_block", + "minecraft:command_block", + "minecraft:chain_command_block", + "minecraft:repeating_command_block", + "minecraft:light", + "minecraft:jigsaw", + "minecraft:barrier", + "minecraft:bedrock", + "minecraft:end_portal_frame", + "minecraft:debug_stick", + "minecraft:command_block_minecart", + "unicopia:plunder_vine", + "#unicopia:badges" + ] +} diff --git a/src/main/resources/data/unicopia/tags/items/loot_bug_high_value_drops.json b/src/main/resources/data/unicopia/tags/items/loot_bug_high_value_drops.json index 38c39b74..c0417584 100644 --- a/src/main/resources/data/unicopia/tags/items/loot_bug_high_value_drops.json +++ b/src/main/resources/data/unicopia/tags/items/loot_bug_high_value_drops.json @@ -23,6 +23,7 @@ "unicopia:golden_polearm", "unicopia:golden_feather", "unicopia:golden_wing", + "unicopia:golden_oak_seeds", { "id": "farmersdelight:golden_knife", "required": false } ] } diff --git a/src/main/resources/data/unicopia/tags/items/palm_logs.json b/src/main/resources/data/unicopia/tags/items/palm_logs.json new file mode 100644 index 00000000..38397972 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/items/palm_logs.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_log", + "unicopia:palm_wood", + "unicopia:stripped_palm_log", + "unicopia:stripped_palm_wood" + ] +} diff --git a/src/main/resources/data/unicopia/traits/chitin.json b/src/main/resources/data/unicopia/traits/chitin.json new file mode 100644 index 00000000..aeddbfd4 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/chitin.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "traits": "earth:9 darkness:8 kindness:-3", + "items": [ + "#unicopia:chitin", + "unicopia:slime_pustule", + "unicopia:carapace" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/edible_plant_based.json b/src/main/resources/data/unicopia/traits/edible_plant_based.json deleted file mode 100644 index ca6cee4d..00000000 --- a/src/main/resources/data/unicopia/traits/edible_plant_based.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "replace": false, - "items": { - "unicopia:green_apple": "life:3", - "unicopia:sweet_apple": "life:3", - "unicopia:sour_apple": "life:3", - "unicopia:zap_apple": "chaos:5", - "unicopia:rotten_apple": "rot:2", - "unicopia:cooked_zap_apple": "chaos:10", - "unicopia:daffodil_daisy_sandwich": "life:2 earth:1", - "unicopia:hay_burger": "life:2 earth:1", - "unicopia:hay_fries": "life:1 earth:2", - "unicopia:wheat_worms": "life:4 earth:6", - "unicopia:cider": "chaos:1 darkness:1", - "unicopia:juice": "chaos:-1 darkness:-9", - "unicopia:burned_juice": "chaos:-1 darkness:-19" - } -} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/food.json b/src/main/resources/data/unicopia/traits/food.json new file mode 100644 index 00000000..0b8e0830 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/food.json @@ -0,0 +1,33 @@ +{ + "replace": false, + "items": { + "unicopia:green_apple": "life:3", + "unicopia:sweet_apple": "life:3", + "unicopia:sour_apple": "life:3", + "unicopia:zap_apple": "chaos:5", + "unicopia:rotten_apple": "rot:2", + "unicopia:cooked_zap_apple": "chaos:10", + "unicopia:daffodil_daisy_sandwich": "life:2 earth:1", + "unicopia:hay_burger": "life:2 earth:1", + "unicopia:hay_fries": "life:1 earth:2", + "unicopia:crispy_hay_fries": "life:1 earth:2 happiness:2", + "unicopia:wheat_worms": "life:4 earth:6", + "unicopia:cider": "chaos:1 darkness:1", + "unicopia:juice": "chaos:-1 darkness:-9", + "unicopia:burned_juice": "chaos:-1 darkness:-19", + "unicopia:pineapple_crown": "life:6", + "unicopia:banana": "life:5 generosity:3", + "unicopia:mango": "earth:2 life:1", + "unicopia:pineapple": "life4 generosity:10", + "unicopia:pinecone": "life:1 happiness:-1", + "#unicopia:oats": "life:1 happiness:1", + "unicopia:apple_pie_slice": "life4 generosity:10", + "unicopia:oatmeal": "happiness:4", + "unicopia:imported_oats": "life:1 happiness:2", + "#unicopia:food_types/candy": "earth:7 strength:3", + "unicopia:zap_bulb": "life:6 power:10", + "unicopia:muffin": "happiness:11", + "unicopia:horse_shoe_fries": "earth:1 strength:11", + "#unicopia:acorns": "life:1 earth:4 strength:3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/from_the_ground.json b/src/main/resources/data/unicopia/traits/from_the_ground.json index a228169b..a5493530 100644 --- a/src/main/resources/data/unicopia/traits/from_the_ground.json +++ b/src/main/resources/data/unicopia/traits/from_the_ground.json @@ -6,7 +6,11 @@ "unicopia:gemstone": "order:1 power:-1", "unicopia:pebbles": "earth:3", "unicopia:rock": "earth:6", + "unicopia:tom": "earth:11", "unicopia:weird_rock": "earth:16 chaos:9", - "unicopia:rock_stew": "earth:9 chaos:16" + "unicopia:rock_stew": "earth:9 chaos:16", + "unicopia:toast": "earth:3", + "unicopia:burned_toast": "chaos:1 earth:3", + "unicopia:jam_toast": "earth:3 strength:1 power:11" } } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/gilded.json b/src/main/resources/data/unicopia/traits/gilded.json new file mode 100644 index 00000000..797b985b --- /dev/null +++ b/src/main/resources/data/unicopia/traits/gilded.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "traits": "strength:10 generosity:-3", + "items": [ + "unicopia:golden_oak_sapling", + "unicopia:golden_oak_leaves", + "unicopia:golden_oak_log", + "unicopia:golden_wing", + "unicopia:golden_oak_seeds" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/intellectual_objects_ordered.json b/src/main/resources/data/unicopia/traits/intellectual_objects_ordered.json new file mode 100644 index 00000000..3d650d72 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/intellectual_objects_ordered.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "knowledge:9 order:9", + "items": [ + "unicopia:weather_vane", + "unicopia:giant_balloon" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/love.json b/src/main/resources/data/unicopia/traits/love.json new file mode 100644 index 00000000..534e5a40 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/love.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "happiness:10 kindness:10", + "items": [ + "#c:love", + "#unicopia:clouds" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/magical.json b/src/main/resources/data/unicopia/traits/magical.json new file mode 100644 index 00000000..3de09a57 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/magical.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "happiness:10 kindness:10", + "items": [ + "#unicopia:groups/unicorn", + "unicopia:curing_joke" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/organic_living.json b/src/main/resources/data/unicopia/traits/organic_living.json new file mode 100644 index 00000000..8cbca2c4 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/organic_living.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "life:10", + "items": [ + "unicopia:mysterious_egg", + "unicopia:tom" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/organic_plant_derived.json b/src/main/resources/data/unicopia/traits/organic_plant_derived.json deleted file mode 100644 index c2167d62..00000000 --- a/src/main/resources/data/unicopia/traits/organic_plant_derived.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "replace": false, - "traits": "earth:2 life:1", - "items": [ - "unicopia:green_apple_leaves", - "unicopia:sweet_apple_leaves", - "unicopia:sour_apple_leaves", - "unicopia:mango" - ] -} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/organic_plant_derived_artificial.json b/src/main/resources/data/unicopia/traits/organic_plant_derived_artificial.json new file mode 100644 index 00000000..5b8fcd5e --- /dev/null +++ b/src/main/resources/data/unicopia/traits/organic_plant_derived_artificial.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "order:1 knowledge:3", + "items": [ + "#unicopia:baskets" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/organic_plant_derived_zap.json b/src/main/resources/data/unicopia/traits/organic_plant_derived_zap.json index 172f955c..a5f068dc 100644 --- a/src/main/resources/data/unicopia/traits/organic_plant_derived_zap.json +++ b/src/main/resources/data/unicopia/traits/organic_plant_derived_zap.json @@ -6,6 +6,7 @@ "unicopia:zap_log", "unicopia:stripped_zap_log", "unicopia:zap_wood", - "unicopia:stripped_zap_wood" + "unicopia:stripped_zap_wood", + "unicopia:flowering_zap_leaves" ] } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/shells.json b/src/main/resources/data/unicopia/traits/shells.json new file mode 100644 index 00000000..94fb1693 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/shells.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "water:7 earth:1 life:1", + "items": [ + "#unicopia:food_types/shells", + "unicopia:shelly" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/soft_and_kind.json b/src/main/resources/data/unicopia/traits/soft_and_kind.json new file mode 100644 index 00000000..c7c44f84 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/soft_and_kind.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "traits": "kindness:3", + "items": [ + "#unicopia:bed_sheets", + "#unicopia:floats_on_clouds", + "#unicopia:pies" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/special.json b/src/main/resources/data/unicopia/traits/special.json index d8f7a165..961eb18e 100644 --- a/src/main/resources/data/unicopia/traits/special.json +++ b/src/main/resources/data/unicopia/traits/special.json @@ -11,6 +11,11 @@ "unicopia:butterfly": "darkness:4 blood:7", "unicopia:spellbook": "power:18 darkness:7", "unicopia:pegasus_amulet": "power:18 order:10 power:9", - "unicopia:alicorn_amulet": "strength:23 order:-10 power:11 darkness:22" + "unicopia:alicorn_amulet": "strength:23 order:-10 power:11 darkness:22", + "#unicopia:cools_off_kirins": "water:6", + "unicopia:hive": "earth:9 darkness:8 kindness:-3 life:10", + "#unicopia:shades": "darkness:3", + "unicopia:broken_sunglasses": "darkness:3 chaos:3", + "unicopia:salt_cube": "power:3" } } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/uplifting_trinkets.json b/src/main/resources/data/unicopia/traits/uplifting_trinkets.json index ec28da31..9360f22f 100644 --- a/src/main/resources/data/unicopia/traits/uplifting_trinkets.json +++ b/src/main/resources/data/unicopia/traits/uplifting_trinkets.json @@ -5,6 +5,8 @@ "unicopia:music_disc_pet": "order:10 kindness:8 focus:9", "unicopia:music_disc_popular": "order:10 generosity:8 focus:9", "unicopia:music_disc_funk": "chaos:-10", - "unicopia:friendship_bracelet": "generosity:1 order:2 happiness:1" + "unicopia:friendship_bracelet": "generosity:1 order:2 happiness:1", + "unicopia:pearl_necklace": "chaos:7 knowledge:2", + "unicopia:lightning_jar": "chaos:1 knowledge:2 power:8" } } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/pit_decors.json b/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/pit_decors.json index c2d1801b..f5380f91 100644 --- a/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/pit_decors.json +++ b/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/pit_decors.json @@ -52,6 +52,17 @@ "projection": "rigid" }, "weight": 1 + }, + { + "element": { + "element_type": "minecraft:single_pool_element", + "location": "unicopia:changeling_hive/pit_decoration/bulb", + "processors": { + "processors": [] + }, + "projection": "rigid" + }, + "weight": 1 } ], "fallback": "minecraft:empty" diff --git a/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/side_passages.json b/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/side_passages.json index 2c4bb501..0a5bd470 100644 --- a/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/side_passages.json +++ b/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/side_passages.json @@ -117,6 +117,15 @@ }, "weight": 3 }, + { + "element": { + "element_type": "minecraft:single_pool_element", + "location": "unicopia:changeling_hive/trapped/offshoot2", + "processors": "unicopia:changeling_hive_decay", + "projection": "rigid" + }, + "weight": 3 + }, { "element": { "element_type": "minecraft:single_pool_element", diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json index d8654fc3..dc5da585 100644 --- a/src/main/resources/unicopia.mixin.json +++ b/src/main/resources/unicopia.mixin.json @@ -73,6 +73,7 @@ "client.MixinItemStack", "client.MixinKeyboardInput", "client.MixinLivingEntityRenderer", + "client.MixinMinecraftClient", "client.MixinModelPart", "client.MixinMouse", "client.MixinPlayerEntityRenderer",