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 @@
+
+
+
+
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..61ab54d5 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.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 0b56490b..902b5a11 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java
@@ -13,7 +13,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 {
BaseZapAppleLeavesBlock() {
super(Settings.create()
@@ -29,61 +29,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;
}
@@ -114,40 +82,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..61658a29
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java
@@ -0,0 +1,47 @@
+package com.minelittlepony.unicopia.block;
+
+import com.minelittlepony.unicopia.ability.EarthPonyGrowAbility.Growable;
+import com.minelittlepony.unicopia.entity.mob.IgnominiousBulbEntity;
+import com.minelittlepony.unicopia.particle.MagicParticleEffect;
+
+import net.minecraft.block.BlockState;
+import net.minecraft.block.FlowerBlock;
+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 {
+ public CuringJokeBlock(StatusEffect suspiciousStewEffect, int effectDuration, Settings settings) {
+ super(suspiciousStewEffect, effectDuration, settings);
+ }
+
+ @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..c6af9166
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java
@@ -0,0 +1,24 @@
+package com.minelittlepony.unicopia.block;
+
+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");
+
+ public EnchantedFruitBlock(Settings settings, Direction attachmentFace, Block stem, VoxelShape shape) {
+ super(settings, attachmentFace, stem, shape);
+ setDefaultState(getDefaultState().with(ENCHANTED, false));
+ }
+
+ @Override
+ protected void appendProperties(StateManager.Builder builder) {
+ super.appendProperties(builder);
+ builder.add(ENCHANTED);
+ }
+
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java b/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java
index ee2eb14d..b9741cef 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java
@@ -62,6 +62,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);
@@ -70,10 +78,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) {
@@ -89,7 +101,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/GoldenOakLeavesBlock.java b/src/main/java/com/minelittlepony/unicopia/block/GoldenOakLeavesBlock.java
new file mode 100644
index 00000000..1ae9ed61
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/block/GoldenOakLeavesBlock.java
@@ -0,0 +1,26 @@
+package com.minelittlepony.unicopia.block;
+
+import java.util.function.Supplier;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.random.Random;
+
+public class GoldenOakLeavesBlock extends FruitBearingBlock {
+
+ public GoldenOakLeavesBlock(Settings settings, int overlay, Supplier fruit,
+ Supplier rottenFruitSupplier) {
+ super(settings, overlay, fruit, rottenFruitSupplier);
+ }
+
+ @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 b74d0566..02ddcdb6 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;
@@ -78,7 +80,7 @@ public interface UBlocks {
Block PALM_STAIRS = register("palm_stairs", new StairsBlock(PALM_PLANKS.getDefaultState(), Settings.copy(PALM_PLANKS).pistonBehavior(PistonBehavior.NORMAL)), ItemGroups.BUILDING_BLOCKS);
Block PALM_SLAB = register("palm_slab", new SlabBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).strength(2, 3).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.NORMAL)), ItemGroups.BUILDING_BLOCKS);
Block PALM_FENCE = register("palm_fence", new FenceBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).strength(2, 3).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.NORMAL)), ItemGroups.BUILDING_BLOCKS);
- Block PALM_FENCE_GATE = register("palm_fence_gate", new FenceGateBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).strength(2, 3).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.NORMAL), WoodType.OAK), ItemGroups.BUILDING_BLOCKS);
+ Block PALM_FENCE_GATE = register("palm_fence_gate", new FenceGateBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).strength(2, 3).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.NORMAL), UWoodTypes.PALM), ItemGroups.BUILDING_BLOCKS);
Block PALM_DOOR = register("palm_door", new DoorBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).instrument(Instrument.BASS).strength(3.0f).nonOpaque().burnable().pistonBehavior(PistonBehavior.DESTROY), UWoodTypes.PALM.setType()), ItemGroups.FUNCTIONAL);
Block PALM_TRAPDOOR = register("palm_trapdoor", new TrapdoorBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).instrument(Instrument.BASS).strength(3).nonOpaque().allowsSpawning(BlockConstructionUtils::never).burnable(), UWoodTypes.PALM.setType()), ItemGroups.FUNCTIONAL);
Block PALM_PRESSURE_PLATE = register("palm_pressure_plate", new PressurePlateBlock(PressurePlateBlock.ActivationRule.EVERYTHING, Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).noCollision().strength(0.5f).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY), BlockSetType.OAK), ItemGroups.BUILDING_BLOCKS);
@@ -126,6 +128,16 @@ public interface UBlocks {
Block SOUR_APPLE = register("sour_apple", new FruitBlock(Settings.create().mapColor(MapColor.GREEN), Direction.DOWN, SOUR_APPLE_LEAVES, FruitBlock.DEFAULT_SHAPE));
Block SOUR_APPLE_SPROUT = register("sour_apple_sprout", new SproutBlock(0xE5FFCC88, () -> UItems.SOUR_APPLE_SEEDS, () -> UTreeGen.SOUR_APPLE_TREE.sapling().map(Block::getDefaultState).get()));
+ Block GOLDEN_OAK_LEAVES = register("golden_oak_leaves", new GoldenOakLeavesBlock(FabricBlockSettings.copy(Blocks.OAK_LEAVES),
+ MapColor.GOLD.color,
+ () -> UBlocks.GOLDEN_APPLE,
+ () -> Items.GOLDEN_APPLE.getDefaultStack()
+ ), ItemGroups.NATURAL);
+ Block GOLDEN_APPLE = register("golden_apple", new EnchantedFruitBlock(Settings.create().mapColor(MapColor.GOLD), Direction.DOWN, GOLDEN_OAK_LEAVES, FruitBlock.DEFAULT_SHAPE));
+ Block GOLDEN_OAK_SPROUT = register("golden_oak_sprout", new SproutBlock(0xE5FFCC88, () -> UItems.GOLDEN_OAK_SEEDS, () -> UTreeGen.GOLDEN_APPLE_TREE.sapling().map(Block::getDefaultState).get()));
+
+ Block GOLDEN_OAK_LOG = register("golden_oak_log", BlockConstructionUtils.createLogBlock(MapColor.OFF_WHITE, MapColor.GOLD), ItemGroups.BUILDING_BLOCKS);
+
Block APPLE_PIE = register("apple_pie", new PieBlock(Settings.create().solid().mapColor(MapColor.ORANGE).strength(0.5F).sounds(BlockSoundGroup.WOOL).pistonBehavior(PistonBehavior.DESTROY),
() -> UItems.APPLE_PIE_SLICE,
() -> UItems.APPLE_PIE,
@@ -138,6 +150,13 @@ public interface UBlocks {
Block PLUNDER_VINE = register("plunder_vine", new ThornBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).ticksRandomly().sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY), () -> UBlocks.PLUNDER_VINE_BUD));
Block PLUNDER_VINE_BUD = register("plunder_vine_bud", new ThornBudBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).nonOpaque().ticksRandomly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY), PLUNDER_VINE.getDefaultState()));
+ 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(Settings.copy(CHITIN), () -> CHITIN), ItemGroups.NATURAL);
@@ -206,6 +225,8 @@ public interface UBlocks {
Block CRYSTAL_DOOR = register("crystal_door", new CrystalDoorBlock(Settings.copy(Blocks.IRON_DOOR), UWoodTypes.CRYSTAL), ItemGroups.FUNCTIONAL);
Block CLOUD_DOOR = register("cloud_door", new CloudDoorBlock(Settings.copy(CLOUD), CLOUD.getDefaultState(), UWoodTypes.CLOUD), ItemGroups.FUNCTIONAL);
+ 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);
}
@@ -229,10 +250,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);
@@ -240,21 +266,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 4e83cbcf..e94f3475 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java
@@ -4,11 +4,8 @@ import com.minelittlepony.unicopia.server.world.ZapAppleStageStore;
import net.minecraft.block.*;
import net.minecraft.item.ItemPlacementContext;
-import net.minecraft.server.world.ServerWorld;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.*;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.world.World;
public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock {
public static final EnumProperty STAGE = EnumProperty.of("stage", ZapAppleStageStore.Stage.class);
@@ -17,31 +14,13 @@ public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock {
setDefaultState(getDefaultState().with(STAGE, ZapAppleStageStore.Stage.GREENING));
}
- @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 5b6a7cdc..40fa9fc2 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java
@@ -1,51 +1,34 @@
package com.minelittlepony.unicopia.block;
import com.minelittlepony.unicopia.server.world.ZapAppleStageStore;
+import com.minelittlepony.unicopia.server.world.ZapAppleStageStore.Stage;
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 {
ZapAppleLeavesPlaceholderBlock() {
super(Settings.create().replaceable().noCollision().dropsNothing().air());
}
@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/PoreousCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java
index 5e17ca06..15303184 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java
@@ -4,6 +4,8 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
+import com.minelittlepony.unicopia.block.state.StateUtil;
+
import net.minecraft.block.BlockState;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
@@ -22,7 +24,7 @@ public class PoreousCloudBlock extends CloudBlock implements Soakable {
@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..3ee498a1 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java
@@ -4,6 +4,8 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
+import com.minelittlepony.unicopia.block.state.StateUtil;
+
import net.minecraft.block.BlockState;
public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakable {
@@ -19,7 +21,7 @@ public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakabl
@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/Soakable.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java
index e800bbf8..94ce19e8 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java
@@ -15,7 +15,6 @@ 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;
@@ -105,15 +104,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 e508b96a..5f769024 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java
@@ -4,6 +4,8 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
+import com.minelittlepony.unicopia.block.state.StateUtil;
+
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
@@ -43,9 +45,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 d074b2cb..7595c2b1 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java
@@ -4,6 +4,8 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
+import com.minelittlepony.unicopia.block.state.StateUtil;
+
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
@@ -43,9 +45,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 495c0f37..9e51e398 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java
@@ -4,6 +4,8 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
+import com.minelittlepony.unicopia.block.state.StateUtil;
+
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
@@ -36,8 +38,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/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