mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 23:27:59 +01:00
Merge branch '1.20.1' into 1.20.2
# Conflicts: # src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Recipe.java # src/main/resources/assets/unicopia/lang/zh_cn.json
This commit is contained in:
commit
ad1784b417
471 changed files with 9175 additions and 2817 deletions
80
assets/accretion_disk.svg
Normal file
80
assets/accretion_disk.svg
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="793.70081"
|
||||||
|
height="1122.5197"
|
||||||
|
viewBox="0 0 210 297"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="accretion_disk.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter1006"
|
||||||
|
x="-0.047823932"
|
||||||
|
width="1.0956479"
|
||||||
|
y="-0.048177369"
|
||||||
|
height="1.0963547">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="11.80918"
|
||||||
|
id="feGaussianBlur1008" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.65297002"
|
||||||
|
inkscape:cx="638.40178"
|
||||||
|
inkscape:cy="466.56457"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
objecttolerance="1"
|
||||||
|
inkscape:window-width="2048"
|
||||||
|
inkscape:window-height="1076"
|
||||||
|
inkscape:window-x="-4"
|
||||||
|
inkscape:window-y="4"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
units="px" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
style="fill:#d98282;stroke:#000000;stroke-width:5.90000009;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;filter:url(#filter1006);stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="M 273.33203 61.957031 C 218.19856 132.07255 244.67649 272.3247 300.8125 327.65625 C 212.65683 342.56237 117.8187 290.41151 96.970703 214.49609 C 106.48046 303.18347 223.38426 385.06833 302.34375 385.77344 C 250.43451 458.78189 146.3945 488.61281 78.105469 449.44141 C 147.35021 505.66464 287.99747 481.38179 344.34961 426.17578 C 358.39121 514.5561 305.1554 608.79183 229.00586 628.76758 C 317.79645 620.27585 401.01904 504.31983 402.66211 425.35938 C 475.15092 478.02297 503.88024 582.37041 463.99023 650.24219 C 520.94121 581.59474 498.14594 440.69846 443.26758 384.42969 C 531.7397 373.62573 624.38067 429.5873 642.12695 506.28711 C 636.22847 417.28661 522.7479 330.71796 443.91992 326.89844 C 498.97294 256.29628 604.20699 230.99439 670.73828 273.08203 C 603.98948 213.9172 462.42095 232.0939 402.21289 284.1875 C 386.71456 195.15366 439.99868 100.94561 516.1582 81.007812 C 427.36338 89.45531 344.08309 205.36983 342.04102 283.99023 C 267.83445 232.87989 235.23336 130.85029 273.33203 61.957031 z M 371.38086 289.78711 A 64.321476 64.321476 0 0 1 435.70117 354.10742 A 64.321476 64.321476 0 0 1 371.38086 418.42969 A 64.321476 64.321476 0 0 1 307.05859 354.10742 A 64.321476 64.321476 0 0 1 371.38086 289.78711 z "
|
||||||
|
transform="scale(0.26458333)"
|
||||||
|
id="path828"
|
||||||
|
inkscape:export-xdpi="37.806454"
|
||||||
|
inkscape:export-ydpi="37.806454" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/models/bulb_angry.png
Normal file
BIN
assets/models/bulb_angry.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
BIN
assets/models/bulb_idle.png
Normal file
BIN
assets/models/bulb_idle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
|
@ -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<T extends Entity> extends EntityModel<T> {
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Entity> {
|
|
||||||
private final ModelPart lid;
|
|
||||||
private final ModelPart lock_r1;
|
|
||||||
private final ModelPart bb_main;
|
|
||||||
public cloud_chest(ModelPart root) {
|
|
||||||
this.lid = root.getChild("lid");
|
|
||||||
this.bb_main = root.getChild("bb_main");
|
|
||||||
}
|
|
||||||
public static TexturedModelData getTexturedModelData() {
|
|
||||||
ModelData modelData = new ModelData();
|
|
||||||
ModelPartData modelPartData = modelData.getRoot();
|
|
||||||
ModelPartData lid = modelPartData.addChild("lid", ModelPartBuilder.create().uv(0, 0).cuboid(-1.0F, -2.0F, 13.8F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
|
|
||||||
.uv(0, 0).cuboid(-1.0F, -1.0F, 14.0F, 2.0F, 2.0F, 1.0F, new Dilation(0.0F))
|
|
||||||
.uv(0, 0).cuboid(-7.0F, -5.0F, 0.0F, 14.0F, 5.0F, 14.0F, new Dilation(0.3F)), ModelTransform.of(0.0F, 16.0F, -7.0F, 1.0908F, 0.0F, 0.0F));
|
|
||||||
|
|
||||||
ModelPartData lock_r1 = lid.addChild("lock_r1", ModelPartBuilder.create().uv(0, 0).cuboid(-2.0F, -4.0F, -0.5F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F)), ModelTransform.of(-2.0F, 1.0F, 14.3F, 0.0F, 0.0F, 1.5708F));
|
|
||||||
|
|
||||||
ModelPartData bb_main = modelPartData.addChild("bb_main", ModelPartBuilder.create().uv(0, 19).cuboid(-7.0F, -10.0F, -7.0F, 14.0F, 10.0F, 14.0F, new Dilation(0.0F)), ModelTransform.pivot(0.0F, 24.0F, 0.0F));
|
|
||||||
return TexturedModelData.of(modelData, 64, 64);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void setAngles(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) {
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) {
|
|
||||||
lid.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
|
|
||||||
bb_main.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
|
|
||||||
}
|
|
||||||
}
|
|
1
assets/models/hay_bale.bbmodel
Normal file
1
assets/models/hay_bale.bbmodel
Normal file
File diff suppressed because one or more lines are too long
113
assets/models/hay_bale.json
Normal file
113
assets/models/hay_bale.json
Normal file
|
@ -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"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1
assets/models/ignimious_bulb.bbmodel
Normal file
1
assets/models/ignimious_bulb.bbmodel
Normal file
File diff suppressed because one or more lines are too long
|
@ -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"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
1
assets/models/tentacle.bbmodel
Normal file
1
assets/models/tentacle.bbmodel
Normal file
File diff suppressed because one or more lines are too long
BIN
assets/models/tentacle.png
Normal file
BIN
assets/models/tentacle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
assets/spectral_clock_0.xcf
Normal file
BIN
assets/spectral_clock_0.xcf
Normal file
Binary file not shown.
|
@ -45,6 +45,16 @@ public class Config extends com.minelittlepony.common.util.settings.Config {
|
||||||
.addComment("Removes butterflies from spawning in your world")
|
.addComment("Removes butterflies from spawning in your world")
|
||||||
.addComment("Turn this ON if you have another mod that adds butterflies.");
|
.addComment("Turn this ON if you have another mod that adds butterflies.");
|
||||||
|
|
||||||
|
public final Setting<Boolean> simplifiedPortals = value("compatibility", "simplifiedPortals", false)
|
||||||
|
.addComment("Disables dynamic portal rendering");
|
||||||
|
|
||||||
|
public final Setting<Long> 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<Integer> maxPortalRecursion = value("client", "maxPortalRecursion", 2)
|
||||||
|
.addComment("Sets the maximum depth to reach when rendering portals through portals");
|
||||||
|
|
||||||
public Config() {
|
public Config() {
|
||||||
super(new HeirarchicalJsonConfigAdapter(new GsonBuilder()
|
super(new HeirarchicalJsonConfigAdapter(new GsonBuilder()
|
||||||
.registerTypeAdapter(Race.class, RegistryTypeAdapter.of(Race.REGISTRY))
|
.registerTypeAdapter(Race.class, RegistryTypeAdapter.of(Race.REGISTRY))
|
||||||
|
|
|
@ -1,33 +1,35 @@
|
||||||
package com.minelittlepony.unicopia;
|
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.ability.magic.spell.trait.SpellTraits;
|
||||||
import com.minelittlepony.unicopia.entity.mob.AirBalloonEntity;
|
import com.minelittlepony.unicopia.entity.mob.AirBalloonEntity;
|
||||||
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||||
|
|
||||||
import net.minecraft.entity.vehicle.BoatEntity;
|
import net.minecraft.entity.vehicle.BoatEntity;
|
||||||
import net.minecraft.registry.Registries;
|
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.World;
|
||||||
|
import net.minecraft.world.dimension.DimensionTypes;
|
||||||
|
|
||||||
public interface Debug {
|
public interface Debug {
|
||||||
boolean SPELLBOOK_CHAPTERS = Boolean.getBoolean("unicopia.debug.spellbookChapters");
|
boolean SPELLBOOK_CHAPTERS = Boolean.getBoolean("unicopia.debug.spellbookChapters");
|
||||||
boolean CHECK_GAME_VALUES = Boolean.getBoolean("unicopia.debug.checkGameValues");
|
boolean CHECK_GAME_VALUES = Boolean.getBoolean("unicopia.debug.checkGameValues");
|
||||||
|
boolean CHECK_TRAIT_COVERAGE = Boolean.getBoolean("unicopia.debug.checkTraitCoverage");
|
||||||
|
|
||||||
boolean[] TESTS_COMPLETE = {false};
|
AtomicReference<World> LAST_TESTED_WORLD = new AtomicReference<>(null);
|
||||||
|
|
||||||
static void runTests(World world) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
TESTS_COMPLETE[0] = true;
|
|
||||||
|
|
||||||
try {
|
if (CHECK_TRAIT_COVERAGE) {
|
||||||
Registries.ITEM.getEntrySet().forEach(entry -> {
|
testTraitCoverage();
|
||||||
if (SpellTraits.of(entry.getValue()).isEmpty()) {
|
|
||||||
// Unicopia.LOGGER.warn("No traits registered for item {}", entry.getKey());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Throwable t) {
|
|
||||||
throw new IllegalStateException("Tests failed", t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -40,4 +42,30 @@ public interface Debug {
|
||||||
throw new IllegalStateException("Tests failed", t);
|
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));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ public interface EntityConvertable<E extends Entity> extends WorldConvertable {
|
||||||
* Gets the center position where this caster is located.
|
* Gets the center position where this caster is located.
|
||||||
*/
|
*/
|
||||||
default Vec3d getOriginVector() {
|
default Vec3d getOriginVector() {
|
||||||
return asEntity().getPos();
|
return asEntity().getPos().add(0, asEntity().getHeight() * 0.5F, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -33,7 +33,7 @@ public interface EquinePredicates {
|
||||||
Predicate<Entity> IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || IS_PLAYER.test(e));
|
Predicate<Entity> IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || IS_PLAYER.test(e));
|
||||||
Predicate<Entity> IS_PLACED_SPELL = e -> e instanceof Caster && !e.isRemoved();
|
Predicate<Entity> IS_PLACED_SPELL = e -> e instanceof Caster && !e.isRemoved();
|
||||||
|
|
||||||
Predicate<Entity> IS_MAGIC_IMMUNE = e -> (e instanceof MagicImmune || !(e instanceof LivingEntity)) && !(e instanceof ItemEntity);
|
Predicate<Entity> IS_MAGIC_IMMUNE = e -> (e instanceof MagicImmune || !(e instanceof LivingEntity)) && !(e instanceof ItemEntity) && !(e instanceof ExperienceOrbEntity);
|
||||||
Predicate<Entity> EXCEPT_MAGIC_IMMUNE = IS_MAGIC_IMMUNE.negate();
|
Predicate<Entity> EXCEPT_MAGIC_IMMUNE = IS_MAGIC_IMMUNE.negate();
|
||||||
Predicate<Entity> VALID_LIVING_AND_NOT_MAGIC_IMMUNE = EntityPredicates.VALID_LIVING_ENTITY.and(EXCEPT_MAGIC_IMMUNE);
|
Predicate<Entity> VALID_LIVING_AND_NOT_MAGIC_IMMUNE = EntityPredicates.VALID_LIVING_ENTITY.and(EXCEPT_MAGIC_IMMUNE);
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
package com.minelittlepony.unicopia;
|
package com.minelittlepony.unicopia;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.ability.magic.CasterView;
|
|
||||||
import com.minelittlepony.unicopia.entity.player.dummy.DummyPlayerEntity;
|
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 com.mojang.authlib.GameProfile;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.network.PacketByteBuf;
|
import net.minecraft.network.PacketByteBuf;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.world.BlockView;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class InteractionManager {
|
public class InteractionManager {
|
||||||
|
@ -37,11 +34,8 @@ public class InteractionManager {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<CasterView> getCasterView(BlockView view) {
|
public ParticleSpawner createBoundParticle(UUID id) {
|
||||||
if (view instanceof ServerWorld world) {
|
return ParticleSpawner.EMPTY;
|
||||||
return Optional.of(Ether.get(world));
|
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Identifier, ?> readChapters(PacketByteBuf buf) {
|
public Map<Identifier, ?> readChapters(PacketByteBuf buf) {
|
||||||
|
@ -92,4 +86,8 @@ public class InteractionManager {
|
||||||
public PlayerEntity createPlayer(World world, GameProfile profile) {
|
public PlayerEntity createPlayer(World world, GameProfile profile) {
|
||||||
return new DummyPlayerEntity(world, profile);
|
return new DummyPlayerEntity(world, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendPlayerLookAngles(PlayerEntity player) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import java.util.UUID;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,14 +29,23 @@ public interface Owned<E extends Entity> {
|
||||||
* Since {@link Owned#getMaster()} will only return if the owner is loaded, use this to perform checks
|
* Since {@link Owned#getMaster()} will only return if the owner is loaded, use this to perform checks
|
||||||
* in the owner's absence.
|
* in the owner's absence.
|
||||||
*/
|
*/
|
||||||
default Optional<UUID> getMasterId() {
|
Optional<UUID> getMasterId();
|
||||||
return Optional.of(getMaster()).map(Entity::getUuid);
|
|
||||||
|
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) {
|
default boolean isOwnedBy(@Nullable Object owner) {
|
||||||
return owner instanceof Entity e
|
return owner instanceof Entity e && e.getUuid().equals(getMasterId().orElse(null));
|
||||||
&& getMasterId().isPresent()
|
|
||||||
&& e.getUuid().equals(getMasterId().get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean hasCommonOwner(Owned<?> sibling) {
|
default boolean hasCommonOwner(Owned<?> sibling) {
|
||||||
|
|
|
@ -62,11 +62,18 @@ public interface USounds {
|
||||||
SoundEvent ENTITY_SOMBRA_AMBIENT = register("entity.sombra.ambient");
|
SoundEvent ENTITY_SOMBRA_AMBIENT = register("entity.sombra.ambient");
|
||||||
SoundEvent ENTITY_SOMBRA_LAUGH = register("entity.sombra.laugh");
|
SoundEvent ENTITY_SOMBRA_LAUGH = register("entity.sombra.laugh");
|
||||||
SoundEvent ENTITY_SOMBRA_SNICKER = register("entity.sombra.snicker");
|
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_AMBIENT = BLOCK_AMETHYST_BLOCK_HIT;
|
||||||
SoundEvent ENTITY_CRYSTAL_SHARDS_JOSTLE = BLOCK_AMETHYST_BLOCK_BREAK;
|
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_CHARGING = register("item.amulet.charging");
|
||||||
SoundEvent ITEM_AMULET_RECHARGE = register("item.amulet.recharge");
|
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_STAFF_STRIKE = ENTITY_PLAYER_ATTACK_CRIT;
|
||||||
SoundEvent ITEM_MAGIC_STAFF_CHARGE = ENTITY_GUARDIAN_ATTACK;
|
SoundEvent ITEM_MAGIC_STAFF_CHARGE = ENTITY_GUARDIAN_ATTACK;
|
||||||
|
|
||||||
|
SoundEvent ITEM_CURING_JOKE_CURE = BLOCK_AMETHYST_BLOCK_BREAK;
|
||||||
|
|
||||||
SoundEvent ITEM_ROCK_LAND = BLOCK_STONE_HIT;
|
SoundEvent ITEM_ROCK_LAND = BLOCK_STONE_HIT;
|
||||||
RegistryEntry.Reference<SoundEvent> ITEM_MUFFIN_BOUNCE = BLOCK_NOTE_BLOCK_BANJO;
|
RegistryEntry.Reference<SoundEvent> ITEM_MUFFIN_BOUNCE = BLOCK_NOTE_BLOCK_BANJO;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ public interface UTags {
|
||||||
TagKey<Item> SHADES = item("shades");
|
TagKey<Item> SHADES = item("shades");
|
||||||
TagKey<Item> CHANGELING_EDIBLE = item("food_types/changeling_edible");
|
TagKey<Item> CHANGELING_EDIBLE = item("food_types/changeling_edible");
|
||||||
TagKey<Item> SPOOKED_MOB_DROPS = item("spooked_mob_drops");
|
TagKey<Item> SPOOKED_MOB_DROPS = item("spooked_mob_drops");
|
||||||
|
TagKey<Item> HAS_NO_TRAITS = item("has_no_traits");
|
||||||
TagKey<Item> IS_DELIVERED_AGGRESSIVELY = item("is_delivered_aggressively");
|
TagKey<Item> IS_DELIVERED_AGGRESSIVELY = item("is_delivered_aggressively");
|
||||||
TagKey<Item> FLOATS_ON_CLOUDS = item("floats_on_clouds");
|
TagKey<Item> FLOATS_ON_CLOUDS = item("floats_on_clouds");
|
||||||
TagKey<Item> COOLS_OFF_KIRINS = item("cools_off_kirins");
|
TagKey<Item> COOLS_OFF_KIRINS = item("cools_off_kirins");
|
||||||
|
@ -42,6 +43,8 @@ public interface UTags {
|
||||||
|
|
||||||
TagKey<Block> CRYSTAL_HEART_BASE = block("crystal_heart_base");
|
TagKey<Block> CRYSTAL_HEART_BASE = block("crystal_heart_base");
|
||||||
TagKey<Block> CRYSTAL_HEART_ORNAMENT = block("crystal_heart_ornament");
|
TagKey<Block> CRYSTAL_HEART_ORNAMENT = block("crystal_heart_ornament");
|
||||||
|
TagKey<Block> UNAFFECTED_BY_GROW_ABILITY = block("unaffected_by_grow_ability");
|
||||||
|
TagKey<Block> KICKS_UP_DUST = block("kicks_up_dust");
|
||||||
|
|
||||||
TagKey<Block> POLEARM_MINEABLE = block("mineable/polearm");
|
TagKey<Block> POLEARM_MINEABLE = block("mineable/polearm");
|
||||||
|
|
||||||
|
|
|
@ -209,6 +209,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
|
||||||
warmup = 0;
|
warmup = 0;
|
||||||
|
|
||||||
if (data.isPresent()) {
|
if (data.isPresent()) {
|
||||||
|
InteractionManager.instance().sendPlayerLookAngles(player.asEntity());
|
||||||
Channel.CLIENT_PLAYER_ABILITY.sendToServer(new MsgPlayerAbility<>(ability, data, ActivationType.NONE));
|
Channel.CLIENT_PLAYER_ABILITY.sendToServer(new MsgPlayerAbility<>(ability, data, ActivationType.NONE));
|
||||||
} else {
|
} else {
|
||||||
player.asEntity().playSound(USounds.GUI_ABILITY_FAIL, 1, 1);
|
player.asEntity().playSound(USounds.GUI_ABILITY_FAIL, 1, 1);
|
||||||
|
|
|
@ -76,9 +76,9 @@ public class ChangeFormAbility implements Ability<Hit> {
|
||||||
targets.forEach(target -> {
|
targets.forEach(target -> {
|
||||||
Race supressed = target.getSuppressedRace();
|
Race supressed = target.getSuppressedRace();
|
||||||
if (target == player || supressed.isUnset() == isTransforming) {
|
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.setSpecies(supressed.or(player.getCompositeRace().potential()));
|
||||||
target.setSuppressedRace(isTransforming ? actualRace : Race.UNSET);
|
target.setSuppressedRace(actualRace);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,37 @@
|
||||||
package com.minelittlepony.unicopia.ability;
|
package com.minelittlepony.unicopia.ability;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.DoubleSupplier;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import com.minelittlepony.unicopia.Race;
|
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.Hit;
|
||||||
import com.minelittlepony.unicopia.ability.data.Pos;
|
import com.minelittlepony.unicopia.ability.data.Pos;
|
||||||
import com.minelittlepony.unicopia.block.UBlocks;
|
import com.minelittlepony.unicopia.block.UBlocks;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
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.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.TraceHelper;
|
||||||
|
import com.minelittlepony.unicopia.util.VecHelper;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
|
import net.minecraft.block.CarrotsBlock;
|
||||||
|
import net.minecraft.block.FarmlandBlock;
|
||||||
import net.minecraft.item.BoneMealItem;
|
import net.minecraft.item.BoneMealItem;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.item.Items;
|
import net.minecraft.item.Items;
|
||||||
|
import net.minecraft.particle.ParticleTypes;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.WorldEvents;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Earth Pony ability to grow crops
|
* Earth Pony ability to grow crops
|
||||||
|
@ -57,10 +72,14 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
|
||||||
public boolean apply(Pony player, Pos data) {
|
public boolean apply(Pony player, Pos data) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (BlockPos pos : BlockPos.iterate(
|
if (!applyDirectly(player, data.pos())) {
|
||||||
data.pos().add(-2, -2, -2),
|
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);
|
data.pos().add( 2, 2, 2))) {
|
||||||
|
count += applySingle(player, player.asWorld(), player.asWorld().getBlockState(pos), pos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
@ -69,7 +88,7 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
|
||||||
return true;
|
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);
|
ItemStack stack = new ItemStack(Items.BONE_MEAL);
|
||||||
|
|
||||||
|
@ -77,12 +96,33 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
|
||||||
return growable.grow(w, state, pos) ? 1 : 0;
|
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 (BoneMealItem.useOnFertilizable(stack, w, pos)) {
|
||||||
if (w.random.nextInt(350) == 0) {
|
if (w.random.nextInt(350) == 0) {
|
||||||
if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) {
|
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());
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -94,6 +134,56 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
|
||||||
return 0;
|
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<Vec3d> 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
|
@Override
|
||||||
public void warmUp(Pony player, AbilitySlot slot) {
|
public void warmUp(Pony player, AbilitySlot slot) {
|
||||||
player.getMagicalReserves().getExertion().addPercent(30);
|
player.getMagicalReserves().getExertion().addPercent(30);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.AwaitTickQueue;
|
import com.minelittlepony.unicopia.AwaitTickQueue;
|
||||||
import com.minelittlepony.unicopia.Race;
|
import com.minelittlepony.unicopia.Race;
|
||||||
|
import com.minelittlepony.unicopia.UTags;
|
||||||
import com.minelittlepony.unicopia.ability.data.Hit;
|
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||||
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
|
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
|
||||||
import com.minelittlepony.unicopia.entity.Living;
|
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.particle.UParticles;
|
||||||
import com.minelittlepony.unicopia.server.world.BlockDestructionManager;
|
import com.minelittlepony.unicopia.server.world.BlockDestructionManager;
|
||||||
import com.minelittlepony.unicopia.util.PosHelper;
|
import com.minelittlepony.unicopia.util.PosHelper;
|
||||||
|
import com.minelittlepony.unicopia.util.VecHelper;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
@ -26,6 +28,7 @@ import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.entity.attribute.EntityAttributes;
|
import net.minecraft.entity.attribute.EntityAttributes;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.particle.BlockStateParticleEffect;
|
||||||
import net.minecraft.registry.tag.BlockTags;
|
import net.minecraft.registry.tag.BlockTags;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.Identifier;
|
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.BlockPos;
|
||||||
import net.minecraft.util.math.Box;
|
import net.minecraft.util.math.Box;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.WorldEvents;
|
import net.minecraft.world.WorldEvents;
|
||||||
|
|
||||||
|
@ -156,7 +160,10 @@ public class EarthPonyStompAbility implements Ability<Hit> {
|
||||||
spawnEffectAround(player, center, radius, rad);
|
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.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.subtractEnergyCost(rad);
|
||||||
iplayer.asEntity().addExhaustion(3);
|
iplayer.asEntity().addExhaustion(3);
|
||||||
|
@ -218,6 +225,12 @@ public class EarthPonyStompAbility implements Ability<Hit> {
|
||||||
} else {
|
} else {
|
||||||
w.syncWorldEvent(WorldEvents.BLOCK_BROKEN, pos, Block.getRawIdFromState(state));
|
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
|
@Override
|
||||||
|
|
|
@ -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.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
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
|
* Pegasus ability to perform rainbooms
|
||||||
|
@ -72,7 +68,6 @@ public class PegasusRainboomAbility implements Ability<Hit> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.consumeSuperMove()) {
|
if (player.consumeSuperMove()) {
|
||||||
player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO);
|
|
||||||
SpellType.RAINBOOM.withTraits().apply(player, CastingMethod.INNATE);
|
SpellType.RAINBOOM.withTraits().apply(player, CastingMethod.INNATE);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.InteractionManager;
|
||||||
import com.minelittlepony.unicopia.Race;
|
import com.minelittlepony.unicopia.Race;
|
||||||
import com.minelittlepony.unicopia.ability.data.Pos;
|
import com.minelittlepony.unicopia.ability.data.Pos;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
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.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
|
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
|
@ -92,7 +93,11 @@ public class UnicornDispellAbility implements Ability<Pos> {
|
||||||
public boolean apply(Pony player, Pos data) {
|
public boolean apply(Pony player, Pos data) {
|
||||||
player.setAnimation(Animation.WOLOLO, Animation.Recipient.ANYONE);
|
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 -> {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,4 +19,8 @@ public interface Affine {
|
||||||
default boolean isFriendlyTogether(Affine other) {
|
default boolean isFriendlyTogether(Affine other) {
|
||||||
return getAffinity() != Affinity.BAD && other.getAffinity() != Affinity.BAD;
|
return getAffinity() != Affinity.BAD && other.getAffinity() != Affinity.BAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default boolean applyInversion(Affine other, boolean friendly) {
|
||||||
|
return isEnemy(other) ? !friendly : friendly;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.entity.*;
|
import com.minelittlepony.unicopia.entity.*;
|
||||||
import com.minelittlepony.unicopia.entity.damage.UDamageSources;
|
import com.minelittlepony.unicopia.entity.damage.UDamageSources;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleSource;
|
import com.minelittlepony.unicopia.particle.ParticleSource;
|
||||||
|
import com.minelittlepony.unicopia.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.server.world.ModificationType;
|
import com.minelittlepony.unicopia.server.world.ModificationType;
|
||||||
import com.minelittlepony.unicopia.util.SoundEmitter;
|
import com.minelittlepony.unicopia.util.SoundEmitter;
|
||||||
import com.minelittlepony.unicopia.util.VecHelper;
|
import com.minelittlepony.unicopia.util.VecHelper;
|
||||||
|
@ -99,11 +100,7 @@ public interface Caster<E extends Entity> extends
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean canCastAt(Vec3d pos) {
|
default boolean canCastAt(Vec3d pos) {
|
||||||
return findAllSpellsInRange(500, SpellType.ARCANE_PROTECTION::isOn).noneMatch(caster -> caster
|
return !Ether.get(asWorld()).anyMatch(SpellType.ARCANE_PROTECTION, (spell, caster) -> spell.blocksMagicFor(caster, this, pos));
|
||||||
.getSpellSlot().get(SpellType.ARCANE_PROTECTION, false)
|
|
||||||
.filter(spell -> spell.blocksMagicFor(caster, this, pos))
|
|
||||||
.isPresent()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Stream<Caster<?>> stream(Stream<Entity> entities) {
|
static Stream<Caster<?>> stream(Stream<Entity> entities) {
|
||||||
|
|
|
@ -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 <S extends Spell> Stream<Map.Entry<Caster<?>, S>> findAllSpellsInRange(BlockPos pos, double radius, SpellPredicate<S> type) {
|
|
||||||
return findAllCastersInRange(pos, radius).flatMap(caster -> {
|
|
||||||
return caster.getSpellSlot().stream(type, false).map(spell -> {
|
|
||||||
return Map.entry(caster, spell);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
default Stream<Caster<?>> findAllCastersInRange(BlockPos pos, double radius) {
|
|
||||||
return findAllCastersInRange(pos, radius, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
default Stream<Caster<?>> findAllCastersInRange(BlockPos pos, double radius, @Nullable Predicate<Entity> test) {
|
|
||||||
return Caster.stream(findAllEntitiesInRange(pos, radius, test == null ? EquinePredicates.IS_CASTER : EquinePredicates.IS_CASTER.and(test)));
|
|
||||||
}
|
|
||||||
|
|
||||||
default Stream<Entity> findAllEntitiesInRange(BlockPos pos, double radius, @Nullable Predicate<Entity> test) {
|
|
||||||
return VecHelper.findInRange(null, getWorld(), Vec3d.ofCenter(pos), radius, test).stream();
|
|
||||||
}
|
|
||||||
|
|
||||||
default Stream<Entity> findAllEntitiesInRange(BlockPos pos, double radius) {
|
|
||||||
return findAllEntitiesInRange(pos, radius, null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,6 +39,13 @@ public interface SpellContainer {
|
||||||
*/
|
*/
|
||||||
void put(@Nullable Spell effect);
|
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.
|
* Removes all active effects that match or contain a matching effect.
|
||||||
*
|
*
|
||||||
|
|
|
@ -69,11 +69,20 @@ public abstract class AbstractDelegatingSpell implements Spell,
|
||||||
getDelegates().forEach(Spell::setDead);
|
getDelegates().forEach(Spell::setDead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tickDying(Caster<?> caster) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDead() {
|
public boolean isDead() {
|
||||||
return getDelegates().isEmpty() || getDelegates().stream().allMatch(Spell::isDead);
|
return getDelegates().isEmpty() || getDelegates().stream().allMatch(Spell::isDead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDying() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirty() {
|
public boolean isDirty() {
|
||||||
return dirty || getDelegates().stream().anyMatch(Spell::isDirty);
|
return dirty || getDelegates().stream().anyMatch(Spell::isDirty);
|
||||||
|
@ -110,7 +119,13 @@ public abstract class AbstractDelegatingSpell implements Spell,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
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
|
@Override
|
||||||
|
|
|
@ -13,10 +13,6 @@ import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.network.Channel;
|
import com.minelittlepony.unicopia.network.Channel;
|
||||||
import com.minelittlepony.unicopia.network.MsgCasterLookRequest;
|
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.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
|
|
||||||
|
@ -24,6 +20,7 @@ import net.minecraft.nbt.*;
|
||||||
import net.minecraft.registry.*;
|
import net.minecraft.registry.*;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
@ -41,9 +38,10 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
private RegistryKey<World> dimension;
|
private RegistryKey<World> 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
|
* The cast spell entity
|
||||||
|
@ -58,6 +56,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
public float pitch;
|
public float pitch;
|
||||||
public float yaw;
|
public float yaw;
|
||||||
|
|
||||||
|
private int prevAge;
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
private boolean dead;
|
||||||
|
private int prevDeathTicks;
|
||||||
|
private int deathTicks;
|
||||||
|
|
||||||
private Optional<Vec3d> position = Optional.empty();
|
private Optional<Vec3d> position = Optional.empty();
|
||||||
|
|
||||||
public PlaceableSpell(CustomisedSpellType<?> type) {
|
public PlaceableSpell(CustomisedSpellType<?> type) {
|
||||||
|
@ -69,23 +74,42 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
return this;
|
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
|
@Override
|
||||||
public Collection<Spell> getDelegates() {
|
public Collection<Spell> getDelegates() {
|
||||||
return List.of(spell);
|
return List.of(spell);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDead() {
|
|
||||||
super.setDead();
|
|
||||||
particlEffect.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
|
|
||||||
if (situation == Situation.BODY) {
|
if (situation == Situation.BODY) {
|
||||||
if (!source.isClient()) {
|
if (!source.isClient()) {
|
||||||
|
|
||||||
if (dimension == null) {
|
if (dimension == null) {
|
||||||
dimension = source.asWorld().getRegistryKey();
|
dimension = source.asWorld().getRegistryKey();
|
||||||
if (source instanceof Pony) {
|
if (source instanceof Pony) {
|
||||||
|
@ -105,29 +129,31 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
|
|
||||||
if (situation == Situation.GROUND_ENTITY) {
|
if (situation == Situation.GROUND_ENTITY) {
|
||||||
if (!source.isClient()) {
|
if (!source.isClient()) {
|
||||||
Ether ether = Ether.get(source.asWorld());
|
if (Ether.get(source.asWorld()).get(this, source) == null) {
|
||||||
if (ether.getEntry(getType(), source).isEmpty()) {
|
|
||||||
setDead();
|
setDead();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell instanceof PlacementDelegate delegate) {
|
prevAge = age;
|
||||||
delegate.updatePlacement(source, this);
|
if (age < 25) {
|
||||||
|
age++;
|
||||||
}
|
}
|
||||||
|
|
||||||
getParticleEffectAttachment(source).ifPresent(p -> {
|
|
||||||
p.setAttribute(Attachment.ATTR_COLOR, spell.getType().getColor());
|
|
||||||
});
|
|
||||||
|
|
||||||
return super.tick(source, Situation.GROUND);
|
return super.tick(source, Situation.GROUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isDead();
|
return !isDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tickDying(Caster<?> caster) {
|
||||||
|
prevDeathTicks = deathTicks;
|
||||||
|
deathTicks--;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkDetachment(Caster<?> source, EntityValues<?> target) {
|
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();
|
setDead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,7 +169,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
entity.getSpellSlot().put(copy);
|
entity.getSpellSlot().put(copy);
|
||||||
entity.setCaster(source);
|
entity.setCaster(source);
|
||||||
entity.getWorld().spawnEntity(entity);
|
entity.getWorld().spawnEntity(entity);
|
||||||
Ether.get(entity.getWorld()).put(getType(), entity);
|
placedSpellId = copy.getUuid();
|
||||||
|
Ether.get(entity.getWorld()).getOrCreate(copy, entity);
|
||||||
|
|
||||||
castEntity.set(entity);
|
castEntity.set(entity);
|
||||||
setDirty();
|
setDirty();
|
||||||
|
@ -174,8 +201,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
if (!source.isClient()) {
|
if (!source.isClient()) {
|
||||||
castEntity.getTarget().ifPresent(target -> {
|
castEntity.getTarget().ifPresent(target -> {
|
||||||
getWorld(source).map(Ether::get)
|
getWorld(source).map(Ether::get)
|
||||||
.flatMap(ether -> ether.getEntry(getType(), target.uuid()))
|
.ifPresent(ether -> ether.remove(getType(), target.uuid()));
|
||||||
.ifPresent(Ether.Entry::markDead);
|
|
||||||
});
|
});
|
||||||
castEntity.set(null);
|
castEntity.set(null);
|
||||||
getSpellEntity(source).ifPresent(e -> {
|
getSpellEntity(source).ifPresent(e -> {
|
||||||
|
@ -183,7 +209,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
});
|
});
|
||||||
|
|
||||||
if (source.asEntity() instanceof CastSpellEntity spellcast) {
|
if (source.asEntity() instanceof CastSpellEntity spellcast) {
|
||||||
Ether.get(source.asWorld()).remove(getType(), source);
|
Ether.get(source.asWorld()).remove(this, source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onDestroyed(source);
|
super.onDestroyed(source);
|
||||||
|
@ -197,12 +223,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
return castEntity.getTarget().map(EntityValues::pos);
|
return castEntity.getTarget().map(EntityValues::pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Attachment> 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<World> getWorld(Caster<?> source) {
|
protected Optional<World> getWorld(Caster<?> source) {
|
||||||
return Optional.ofNullable(dimension)
|
return Optional.ofNullable(dimension)
|
||||||
.map(dim -> source.asWorld().getServer().getWorld(dim));
|
.map(dim -> source.asWorld().getServer().getWorld(dim));
|
||||||
|
@ -211,11 +231,17 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
@Override
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
super.toNBT(compound);
|
super.toNBT(compound);
|
||||||
|
compound.putBoolean("dead", dead);
|
||||||
|
compound.putInt("deathTicks", deathTicks);
|
||||||
|
compound.putInt("age", age);
|
||||||
compound.putFloat("pitch", pitch);
|
compound.putFloat("pitch", pitch);
|
||||||
compound.putFloat("yaw", yaw);
|
compound.putFloat("yaw", yaw);
|
||||||
position.ifPresent(pos -> {
|
position.ifPresent(pos -> {
|
||||||
compound.put("position", NbtSerialisable.writeVector(pos));
|
compound.put("position", NbtSerialisable.writeVector(pos));
|
||||||
});
|
});
|
||||||
|
if (placedSpellId != null) {
|
||||||
|
compound.putUuid("placedSpellId", placedSpellId);
|
||||||
|
}
|
||||||
if (dimension != null) {
|
if (dimension != null) {
|
||||||
compound.putString("dimension", dimension.getValue().toString());
|
compound.putString("dimension", dimension.getValue().toString());
|
||||||
}
|
}
|
||||||
|
@ -226,9 +252,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(compound);
|
super.fromNBT(compound);
|
||||||
|
dead = compound.getBoolean("dead");
|
||||||
|
deathTicks = compound.getInt("deathTicks");
|
||||||
|
age = compound.getInt("age");
|
||||||
pitch = compound.getFloat("pitch");
|
pitch = compound.getFloat("pitch");
|
||||||
yaw = compound.getFloat("yaw");
|
yaw = compound.getFloat("yaw");
|
||||||
position = compound.contains("position") ? Optional.of(NbtSerialisable.readVector(compound.getList("position", NbtElement.FLOAT_TYPE))) : Optional.empty();
|
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)) {
|
if (compound.contains("dimension", NbtElement.STRING_TYPE)) {
|
||||||
Identifier id = Identifier.tryParse(compound.getString("dimension"));
|
Identifier id = Identifier.tryParse(compound.getString("dimension"));
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
|
@ -257,9 +287,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface PlacementDelegate {
|
public interface PlacementDelegate {
|
||||||
|
|
||||||
void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity);
|
void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity);
|
||||||
|
|
||||||
void updatePlacement(Caster<?> source, PlaceableSpell parent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package com.minelittlepony.unicopia.ability.magic.spell;
|
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.UTags;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.*;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.*;
|
||||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle;
|
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
|
import com.minelittlepony.unicopia.particle.ParticleSpawner;
|
||||||
|
import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect;
|
||||||
import com.minelittlepony.unicopia.server.world.ModificationType;
|
import com.minelittlepony.unicopia.server.world.ModificationType;
|
||||||
import com.minelittlepony.unicopia.particle.UParticles;
|
import com.minelittlepony.unicopia.particle.UParticles;
|
||||||
import com.minelittlepony.unicopia.util.shape.Shape;
|
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 int RADIUS = 5;
|
||||||
private static final Shape EFFECT_RANGE = new Sphere(false, RADIUS);
|
private static final Shape EFFECT_RANGE = new Sphere(false, RADIUS);
|
||||||
|
|
||||||
private final ParticleHandle particlEffect = new ParticleHandle();
|
@Nullable
|
||||||
|
private ParticleSpawner boundParticle;
|
||||||
|
|
||||||
private int age;
|
private int age;
|
||||||
|
|
||||||
|
@ -35,11 +40,6 @@ public class RainboomAbilitySpell extends AbstractSpell {
|
||||||
setHidden(true);
|
setHidden(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroyed(Caster<?> source) {
|
|
||||||
particlEffect.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
|
|
||||||
|
@ -47,14 +47,15 @@ public class RainboomAbilitySpell extends AbstractSpell {
|
||||||
return false;
|
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()) {
|
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 -> {
|
source.findAllEntitiesInRange(RADIUS).forEach(e -> {
|
||||||
|
@ -92,5 +93,6 @@ public class RainboomAbilitySpell extends AbstractSpell {
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(compound);
|
super.fromNBT(compound);
|
||||||
age = compound.getInt("age");
|
age = compound.getInt("age");
|
||||||
|
boundParticle = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,8 @@ public interface Spell extends NbtSerialisable, Affine {
|
||||||
*/
|
*/
|
||||||
boolean isDead();
|
boolean isDead();
|
||||||
|
|
||||||
|
boolean isDying();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this effect has changes that need to be sent to the client.
|
* 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.
|
* Applies this spell to the supplied caster.
|
||||||
|
* @param caster The caster to apply the spell to
|
||||||
*/
|
*/
|
||||||
default boolean apply(Caster<?> caster) {
|
default boolean apply(Caster<?> caster) {
|
||||||
caster.getSpellSlot().put(this);
|
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.
|
* 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) {
|
default Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -89,6 +92,12 @@ public interface Spell extends NbtSerialisable, Affine {
|
||||||
*/
|
*/
|
||||||
boolean tick(Caster<?> caster, Situation situation);
|
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.
|
* Marks this effect as dirty.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -29,7 +29,7 @@ public interface TimedSpell extends Spell {
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getPercentTimeRemaining(float tickDelta) {
|
public float getPercentTimeRemaining(float tickDelta) {
|
||||||
return MathHelper.lerp(tickDelta, prevDuration, duration) / maxDuration;
|
return MathHelper.lerp(tickDelta, prevDuration, duration) / (float)maxDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTicksRemaining() {
|
public int getTicksRemaining() {
|
||||||
|
|
|
@ -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<ItemEntity> ingredients,
|
||||||
|
ItemStack result
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static AltarRecipeMatch of(List<ItemEntity> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import net.minecraft.nbt.NbtCompound;
|
||||||
public abstract class AbstractSpell implements Spell {
|
public abstract class AbstractSpell implements Spell {
|
||||||
|
|
||||||
private boolean dead;
|
private boolean dead;
|
||||||
|
private boolean dying;
|
||||||
private boolean dirty;
|
private boolean dirty;
|
||||||
private boolean hidden;
|
private boolean hidden;
|
||||||
private boolean destroyed;
|
private boolean destroyed;
|
||||||
|
@ -45,7 +46,7 @@ public abstract class AbstractSpell implements Spell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void setDead() {
|
public final void setDead() {
|
||||||
dead = true;
|
dying = true;
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +55,11 @@ public abstract class AbstractSpell implements Spell {
|
||||||
return dead;
|
return dead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isDying() {
|
||||||
|
return dying;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isDirty() {
|
public final boolean isDirty() {
|
||||||
return dirty;
|
return dirty;
|
||||||
|
@ -82,6 +88,11 @@ public abstract class AbstractSpell implements Spell {
|
||||||
protected void onDestroyed(Caster<?> caster) {
|
protected void onDestroyed(Caster<?> caster) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tickDying(Caster<?> caster) {
|
||||||
|
dead = true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void destroy(Caster<?> caster) {
|
public final void destroy(Caster<?> caster) {
|
||||||
if (destroyed) {
|
if (destroyed) {
|
||||||
|
@ -94,6 +105,7 @@ public abstract class AbstractSpell implements Spell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
|
compound.putBoolean("dying", dying);
|
||||||
compound.putBoolean("dead", dead);
|
compound.putBoolean("dead", dead);
|
||||||
compound.putBoolean("hidden", hidden);
|
compound.putBoolean("hidden", hidden);
|
||||||
compound.putUuid("uuid", uuid);
|
compound.putUuid("uuid", uuid);
|
||||||
|
@ -106,6 +118,7 @@ public abstract class AbstractSpell implements Spell {
|
||||||
if (compound.contains("uuid")) {
|
if (compound.contains("uuid")) {
|
||||||
uuid = compound.getUuid("uuid");
|
uuid = compound.getUuid("uuid");
|
||||||
}
|
}
|
||||||
|
dying = compound.getBoolean("dying");
|
||||||
dead = compound.getBoolean("dead");
|
dead = compound.getBoolean("dead");
|
||||||
hidden = compound.getBoolean("hidden");
|
hidden = compound.getBoolean("hidden");
|
||||||
if (compound.contains("traits")) {
|
if (compound.contains("traits")) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
|
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
|
||||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
||||||
|
import com.minelittlepony.unicopia.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.util.shape.Sphere;
|
import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
|
@ -42,6 +43,8 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell {
|
||||||
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
|
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 -> {
|
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();
|
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.
|
* Calculates the maximum radius of the shield. aka The area of effect.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,7 +7,6 @@ import com.minelittlepony.unicopia.entity.EntityReference;
|
||||||
import com.minelittlepony.unicopia.entity.Living;
|
import com.minelittlepony.unicopia.entity.Living;
|
||||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
||||||
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
|
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
|
||||||
import com.minelittlepony.unicopia.particle.UParticles;
|
import com.minelittlepony.unicopia.particle.UParticles;
|
||||||
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
||||||
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
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.Entity;
|
||||||
import net.minecraft.entity.ItemEntity;
|
import net.minecraft.entity.ItemEntity;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.particle.ParticleTypes;
|
||||||
import net.minecraft.util.hit.EntityHitResult;
|
import net.minecraft.util.hit.EntityHitResult;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
@ -44,9 +44,10 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
|
||||||
if (timer.getTicksRemaining() <= 0) {
|
if (timer.getTicksRemaining() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
setDirty();
|
|
||||||
target.getOrEmpty(caster.asWorld())
|
target.getOrEmpty(caster.asWorld())
|
||||||
.filter(entity -> entity.distanceTo(caster.asEntity()) > getDrawDropOffRange(caster))
|
.filter(entity -> entity.distanceTo(caster.asEntity()) > getDrawDropOffRange(caster))
|
||||||
.ifPresent(entity -> {
|
.ifPresent(entity -> {
|
||||||
|
@ -59,12 +60,13 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateParticles(Caster<?> source) {
|
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(
|
source.addParticle(
|
||||||
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, source.asEntity(), 0.4F)
|
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F)
|
||||||
.withChild(new MagicParticleEffect(getType().getColor())),
|
.withChild(ParticleTypes.AMBIENT_ENTITY_EFFECT),
|
||||||
p,
|
p,
|
||||||
Vec3d.ZERO
|
Vec3d.ZERO
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,12 +11,10 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||||
import com.minelittlepony.unicopia.entity.*;
|
import com.minelittlepony.unicopia.entity.*;
|
||||||
import com.minelittlepony.unicopia.entity.mob.UEntityAttributes;
|
import com.minelittlepony.unicopia.entity.mob.UEntityAttributes;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
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.UParticles;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
|
|
||||||
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
||||||
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
||||||
|
import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
@ -48,11 +46,11 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
||||||
.with(Trait.POWER, 1)
|
.with(Trait.POWER, 1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
protected final ParticleHandle particlEffect = new ParticleHandle();
|
|
||||||
|
|
||||||
private final Timer timer;
|
private final Timer timer;
|
||||||
|
|
||||||
private int struggles;
|
private int struggles;
|
||||||
|
|
||||||
|
private float prevRadius;
|
||||||
private float radius;
|
private float radius;
|
||||||
|
|
||||||
protected BubbleSpell(CustomisedSpellType<?> type) {
|
protected BubbleSpell(CustomisedSpellType<?> type) {
|
||||||
|
@ -66,6 +64,10 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getRadius(float tickDelta) {
|
||||||
|
return MathHelper.lerp(tickDelta, prevRadius, radius);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Caster<?> source) {
|
public boolean apply(Caster<?> source) {
|
||||||
|
|
||||||
|
@ -95,14 +97,19 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
|
|
||||||
if (situation == Situation.PROJECTILE) {
|
if (situation == Situation.PROJECTILE) {
|
||||||
|
source.spawnParticles(UParticles.BUBBLE, 2);
|
||||||
source.spawnParticles(ParticleTypes.BUBBLE, 2);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
timer.tick();
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +123,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
||||||
|
|
||||||
source.asEntity().fallDistance = 0;
|
source.asEntity().fallDistance = 0;
|
||||||
|
|
||||||
Vec3d origin = source.getOriginVector();
|
prevRadius = radius;
|
||||||
|
|
||||||
if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) {
|
if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) {
|
||||||
setDirty();
|
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();
|
return !isDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroyed(Caster<?> source) {
|
protected void onDestroyed(Caster<?> source) {
|
||||||
particlEffect.destroy();
|
|
||||||
if (source.asEntity() instanceof LivingEntity l) {
|
if (source.asEntity() instanceof LivingEntity l) {
|
||||||
MODIFIERS.forEach((attribute, modifier) -> {
|
MODIFIERS.forEach((attribute, modifier) -> {
|
||||||
if (l.getAttributes().hasAttribute(attribute)) {
|
if (l.getAttributes().hasAttribute(attribute)) {
|
||||||
|
|
|
@ -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.Spell;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
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.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.LightningBoltParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||||
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
|
|
||||||
import com.minelittlepony.unicopia.particle.UParticles;
|
import com.minelittlepony.unicopia.particle.UParticles;
|
||||||
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
||||||
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
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.
|
* 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 class DarkVortexSpell extends AttractiveSpell implements ProjectileDelegate.BlockHitListener {
|
||||||
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
|
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 static final Vec3d SPHERE_OFFSET = new Vec3d(0, 2, 0);
|
||||||
|
|
||||||
private int age = 0;
|
|
||||||
private float accumulatedMass = 0;
|
private float accumulatedMass = 0;
|
||||||
|
|
||||||
protected DarkVortexSpell(CustomisedSpellType<?> type) {
|
protected DarkVortexSpell(CustomisedSpellType<?> type) {
|
||||||
|
@ -84,17 +79,10 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
age++;
|
if (source.asEntity().age % 20 == 0) {
|
||||||
setDirty();
|
|
||||||
|
|
||||||
if (age % 20 == 0) {
|
|
||||||
source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1);
|
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) {
|
if (!source.isClient() && source.asWorld().random.nextInt(300) == 0) {
|
||||||
ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, getOrigin(source), Vec3d.ZERO);
|
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);
|
return super.tick(source, situation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void consumeManage(Caster<?> source, long costMultiplier, float knowledge) {
|
||||||
|
if (!source.subtractEnergyCost(-accumulatedMass)) {
|
||||||
|
setDead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFriendlyTogether(Affine other) {
|
public boolean isFriendlyTogether(Affine other) {
|
||||||
return accumulatedMass < 4;
|
return accumulatedMass < 4;
|
||||||
|
@ -116,21 +111,20 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
|
||||||
public void generateParticles(Caster<?> source) {
|
public void generateParticles(Caster<?> source) {
|
||||||
super.generateParticles(source);
|
super.generateParticles(source);
|
||||||
|
|
||||||
float radius = (float)getEventHorizonRadius();
|
if (getEventHorizonRadius() > 0.3) {
|
||||||
|
double range = getDrawDropOffRange(source);
|
||||||
particlEffect.update(getUuid(), source, spawner -> {
|
Vec3d origin = getOrigin(source);
|
||||||
spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0x000000, 0.99F, radius, SPHERE_OFFSET), source.getOriginVector(), Vec3d.ZERO);
|
source.spawnParticles(origin, new Sphere(false, range), 1, p -> {
|
||||||
}).ifPresent(p -> {
|
if (!source.asWorld().isAir(BlockPos.ofFloored(p))) {
|
||||||
p.setAttribute(Attachment.ATTR_RADIUS, radius);
|
source.addParticle(
|
||||||
p.setAttribute(Attachment.ATTR_OPACITY, 2F);
|
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F)
|
||||||
});
|
.withChild(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE),
|
||||||
particlEffect.update(getUuid(), "_ring", source, spawner -> {
|
p,
|
||||||
spawner.addParticle(new SphereParticleEffect(UParticles.DISK, 0xAAAAAA, 0.4F, radius + 1, SPHERE_OFFSET), getOrigin(source), Vec3d.ZERO);
|
Vec3d.ZERO
|
||||||
}).ifPresent(p -> {
|
);
|
||||||
p.setAttribute(Attachment.ATTR_RADIUS, radius * 0F);
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
source.spawnParticles(ParticleTypes.SMOKE, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -162,7 +156,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
|
||||||
applyRadialEffect(source, e, e.getPos().distanceTo(origin), radius);
|
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
|
// 2. max force (at dist 0) is taken from accumulated mass
|
||||||
// 3. force reaches 0 at distance of drawDropOffRange
|
// 3. force reaches 0 at distance of drawDropOffRange
|
||||||
|
|
||||||
private double getEventHorizonRadius() {
|
public double getEventHorizonRadius() {
|
||||||
return Math.sqrt(Math.max(0.001, getMass() - 12));
|
return Math.sqrt(Math.max(0.001, getMass() / 3F));
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getAttractiveForce(Caster<?> source, Entity target) {
|
private double getAttractiveForce(Caster<?> source, Entity target) {
|
||||||
|
@ -189,8 +182,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getMass() {
|
private double getMass() {
|
||||||
float pulse = (float)Math.sin(age * 8) / 1F;
|
return Math.min(15, 0.1F + accumulatedMass / 10F);
|
||||||
return 10 + Math.min(15, Math.min(0.5F + pulse, (float)Math.exp(age) / 8F - 90) + accumulatedMass / 10F) + pulse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -202,6 +194,11 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
|
||||||
|
|
||||||
if (distance <= getEventHorizonRadius() + 0.5) {
|
if (distance <= getEventHorizonRadius() + 0.5) {
|
||||||
target.setVelocity(target.getVelocity().multiply(distance / (2 * radius)));
|
target.setVelocity(target.getVelocity().multiply(distance / (2 * radius)));
|
||||||
|
if (distance < 1) {
|
||||||
|
target.setVelocity(target.getVelocity().multiply(distance));
|
||||||
|
|
||||||
|
}
|
||||||
|
Living.updateVelocity(target);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Entity master = source.getMaster();
|
Entity master = source.getMaster();
|
||||||
|
@ -221,13 +218,19 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
|
||||||
|
|
||||||
double massOfTarget = AttractionUtils.getMass(target);
|
double massOfTarget = AttractionUtils.getMass(target);
|
||||||
|
|
||||||
accumulatedMass += massOfTarget;
|
if (!source.isClient() && massOfTarget != 0) {
|
||||||
setDirty();
|
accumulatedMass += massOfTarget;
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE);
|
target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE);
|
||||||
if (!(target instanceof PlayerEntity)) {
|
if (!(target instanceof PlayerEntity)) {
|
||||||
target.discard();
|
target.discard();
|
||||||
source.asWorld().playSound(null, source.getOrigin(), USounds.ENCHANTMENT_CONSUMPTION_CONSUME, SoundCategory.AMBIENT, 2, 0.02F);
|
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.subtractEnergyCost(-massOfTarget * 10);
|
||||||
source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.02F);
|
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
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
super.toNBT(compound);
|
super.toNBT(compound);
|
||||||
compound.putInt("age", age);
|
|
||||||
compound.putFloat("accumulatedMass", accumulatedMass);
|
compound.putFloat("accumulatedMass", accumulatedMass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(compound);
|
super.fromNBT(compound);
|
||||||
age = compound.getInt("age");
|
|
||||||
accumulatedMass = compound.getFloat("accumulatedMass");
|
accumulatedMass = compound.getFloat("accumulatedMass");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||||
import net.minecraft.util.math.Vec3d;
|
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 {
|
public class DisperseIllusionSpell extends AbstractAreaEffectSpell {
|
||||||
protected DisperseIllusionSpell(CustomisedSpellType<?> type) {
|
protected DisperseIllusionSpell(CustomisedSpellType<?> type) {
|
||||||
|
|
|
@ -6,8 +6,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.*;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
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.MagicProjectileEntity;
|
||||||
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
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.hit.EntityHitResult;
|
||||||
import net.minecraft.util.math.Vec3d;
|
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<Entity> target = new EntityReference<>();
|
private final EntityReference<Entity> target = new EntityReference<>();
|
||||||
|
|
||||||
|
@ -67,19 +65,6 @@ public class DisplacementSpell extends AbstractSpell implements HomingSpell, Pla
|
||||||
originator.subtractEnergyCost(destinationPos.distanceTo(sourcePos) / 20F);
|
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) {
|
private void teleport(Caster<?> source, Entity entity, Vec3d pos, Vec3d vel) {
|
||||||
entity.teleport(pos.x, pos.y, pos.z);
|
entity.teleport(pos.x, pos.y, pos.z);
|
||||||
entity.setVelocity(vel);
|
entity.setVelocity(vel);
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell,
|
||||||
if (caster instanceof MagicProjectileEntity && getTraits().get(Trait.FOCUS) >= 50) {
|
if (caster instanceof MagicProjectileEntity && getTraits().get(Trait.FOCUS) >= 50) {
|
||||||
caster.findAllEntitiesInRange(
|
caster.findAllEntitiesInRange(
|
||||||
getTraits().get(Trait.FOCUS) - 49,
|
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 -> {
|
).findFirst().ifPresent(target -> {
|
||||||
((MagicProjectileEntity)caster).setHomingTarget(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()) {
|
if (getTraits().get(Trait.FOCUS) >= 50 && target.getOrEmpty(caster.asWorld()).isEmpty()) {
|
||||||
target.set(caster.findAllEntitiesInRange(
|
target.set(caster.findAllEntitiesInRange(
|
||||||
getTraits().get(Trait.FOCUS) - 49,
|
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));
|
).findFirst().orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,16 @@ import java.util.Set;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.USounds;
|
import com.minelittlepony.unicopia.USounds;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
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.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.SpellTraits;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||||
import com.minelittlepony.unicopia.advancement.UCriteria;
|
import com.minelittlepony.unicopia.advancement.UCriteria;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.particle.UParticles;
|
import com.minelittlepony.unicopia.particle.UParticles;
|
||||||
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
||||||
|
import com.minelittlepony.unicopia.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
import com.minelittlepony.unicopia.util.shape.*;
|
import com.minelittlepony.unicopia.util.shape.*;
|
||||||
|
|
||||||
|
@ -21,8 +23,10 @@ import net.minecraft.fluid.*;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.state.property.Properties;
|
import net.minecraft.state.property.Properties;
|
||||||
import net.minecraft.registry.tag.TagKey;
|
import net.minecraft.registry.tag.TagKey;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class HydrophobicSpell extends AbstractSpell {
|
public class HydrophobicSpell extends AbstractSpell {
|
||||||
|
@ -41,16 +45,15 @@ public class HydrophobicSpell extends AbstractSpell {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Caster<?> source) {
|
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||||
if (getTraits().get(Trait.GENEROSITY) > 0) {
|
if ((method == CastingMethod.DIRECT || method == CastingMethod.STAFF) && getTraits().get(Trait.GENEROSITY) > 0) {
|
||||||
return toPlaceable().apply(source);
|
return toPlaceable();
|
||||||
}
|
}
|
||||||
return super.apply(source);
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
|
|
||||||
if (!source.isClient()) {
|
if (!source.isClient()) {
|
||||||
World world = source.asWorld();
|
World world = source.asWorld();
|
||||||
|
|
||||||
|
@ -86,7 +89,11 @@ public class HydrophobicSpell extends AbstractSpell {
|
||||||
setDead();
|
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);
|
BlockPos bp = BlockPos.ofFloored(pos);
|
||||||
if (source.asWorld().getFluidState(bp.up()).isIn(affectedFluid)) {
|
if (source.asWorld().getFluidState(bp.up()).isIn(affectedFluid)) {
|
||||||
source.addParticle(UParticles.RAIN_DROPS, pos, Vec3d.ZERO);
|
source.addParticle(UParticles.RAIN_DROPS, pos, Vec3d.ZERO);
|
||||||
|
@ -107,6 +114,7 @@ public class HydrophobicSpell extends AbstractSpell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroyed(Caster<?> caster) {
|
protected void onDestroyed(Caster<?> caster) {
|
||||||
|
Ether.get(caster.asWorld()).remove(this, caster);
|
||||||
storedFluidPositions.removeIf(entry -> {
|
storedFluidPositions.removeIf(entry -> {
|
||||||
entry.restore(caster.asWorld());
|
entry.restore(caster.asWorld());
|
||||||
return true;
|
return true;
|
||||||
|
@ -162,13 +170,20 @@ public class HydrophobicSpell extends AbstractSpell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean blocksFlow(Caster<?> caster, BlockPos pos, FluidState fluid) {
|
public boolean blocksFlow(Ether.Entry<?> entry, Vec3d center, BlockPos pos, FluidState fluid) {
|
||||||
return fluid.isIn(affectedFluid) && pos.isWithinDistance(caster.getOrigin(), getRange(caster) + 1);
|
return fluid.isIn(affectedFluid) && pos.isWithinDistance(center, (double)entry.radius + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean blocksFluidFlow(CasterView world, BlockPos pos, FluidState state) {
|
public static boolean blocksFluidFlow(BlockView world, BlockPos pos, FluidState state) {
|
||||||
return world.findAllSpellsInRange(pos, 500, SpellType.HYDROPHOBIC).anyMatch(pair -> {
|
if (world instanceof ServerWorld sw) {
|
||||||
return pair.getValue().blocksFlow(pair.getKey(), pos, state);
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.USounds;
|
import com.minelittlepony.unicopia.USounds;
|
||||||
import com.minelittlepony.unicopia.Unicopia;
|
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.SpellTraits;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||||
|
import com.minelittlepony.unicopia.entity.Living;
|
||||||
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
|
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
|
||||||
import com.minelittlepony.unicopia.particle.*;
|
import com.minelittlepony.unicopia.particle.*;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
|
|
||||||
import com.minelittlepony.unicopia.server.world.Ether;
|
import com.minelittlepony.unicopia.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.util.shape.*;
|
import com.minelittlepony.unicopia.util.shape.*;
|
||||||
|
|
||||||
|
@ -20,8 +23,10 @@ import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
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.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.MathHelper;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.WorldEvents;
|
import net.minecraft.world.WorldEvents;
|
||||||
|
@ -34,12 +39,14 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
.build();
|
.build();
|
||||||
private static final Shape PARTICLE_AREA = new Sphere(true, 2, 1, 1, 0);
|
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<Entity> teleportationTarget = new EntityReference<>();
|
private final EntityReference<Entity> teleportationTarget = new EntityReference<>();
|
||||||
|
|
||||||
private boolean publishedPosition;
|
private boolean publishedPosition;
|
||||||
|
|
||||||
private final ParticleHandle particleEffect = new ParticleHandle();
|
|
||||||
|
|
||||||
private float pitch;
|
private float pitch;
|
||||||
private float yaw;
|
private float yaw;
|
||||||
|
|
||||||
|
@ -49,6 +56,39 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLinked() {
|
||||||
|
return teleportationTarget.getTarget().isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<EntityReference.EntityValues<Entity>> 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<Ether.Entry<PortalSpell>> getTarget(Caster<?> source) {
|
||||||
|
return getTarget().map(target -> Ether.get(source.asWorld()).get((SpellType<PortalSpell>)getType(), target, targetPortalId));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Caster<?> caster) {
|
public boolean apply(Caster<?> caster) {
|
||||||
setOrientation(caster.asEntity().getPitch(), caster.asEntity().getYaw());
|
setOrientation(caster.asEntity().getPitch(), caster.asEntity().getYaw());
|
||||||
|
@ -60,29 +100,12 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
if (situation == Situation.GROUND) {
|
if (situation == Situation.GROUND) {
|
||||||
|
|
||||||
if (source.isClient()) {
|
if (source.isClient()) {
|
||||||
Vec3d origin = source.getOriginVector();
|
source.spawnParticles(particleArea, 5, pos -> {
|
||||||
|
source.addParticle(ParticleTypes.ELECTRIC_SPARK, pos, Vec3d.ZERO);
|
||||||
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();
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
teleportationTarget.getTarget().ifPresent(target -> {
|
getTarget().ifPresent(target -> {
|
||||||
if (Ether.get(source.asWorld()).getEntry(getType(), target.uuid()).isEmpty()) {
|
if (Ether.get(source.asWorld()).get(getType(), target, targetPortalId) == null) {
|
||||||
Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid());
|
Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid());
|
||||||
teleportationTarget.set(null);
|
teleportationTarget.set(null);
|
||||||
setDirty();
|
setDirty();
|
||||||
|
@ -96,37 +119,56 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!publishedPosition) {
|
var entry = Ether.get(source.asWorld()).getOrCreate(this, source);
|
||||||
publishedPosition = true;
|
entry.pitch = pitch;
|
||||||
Ether.Entry entry = Ether.get(source.asWorld()).put(getType(), source);
|
entry.yaw = yaw;
|
||||||
entry.pitch = pitch;
|
Ether.get(source.asWorld()).markDirty();
|
||||||
entry.yaw = yaw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isDead();
|
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 -> {
|
destination.entity.getTarget().ifPresent(target -> {
|
||||||
source.findAllEntitiesInRange(1).forEach(entity -> {
|
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());
|
Vec3d offset = entity.getPos().subtract(source.getOriginVector());
|
||||||
float yawDifference = pitch < 15 ? (180 - yaw + destination.yaw) : 0;
|
float yawDifference = pitch < 15 ? getYawDifference() : 0;
|
||||||
Vec3d dest = target.pos().add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.05, 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.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.setVelocity(entity.getVelocity().rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE));
|
||||||
|
|
||||||
entity.getWorld().playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1);
|
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);
|
entity.getWorld().playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1);
|
||||||
setDirty();
|
setDirty();
|
||||||
|
|
||||||
|
Living.updateVelocity(entity);
|
||||||
|
|
||||||
if (!source.subtractEnergyCost(Math.sqrt(entity.getPos().subtract(dest).length()))) {
|
if (!source.subtractEnergyCost(Math.sqrt(entity.getPos().subtract(dest).length()))) {
|
||||||
setDead();
|
setDead();
|
||||||
}
|
}
|
||||||
|
@ -142,20 +184,15 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ether ether = Ether.get(source.asWorld());
|
Ether.get(source.asWorld()).anyMatch(getType(), entry -> {
|
||||||
ether.getEntries(getType())
|
if (entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet()) {
|
||||||
.stream()
|
|
||||||
.filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet())
|
|
||||||
.findAny()
|
|
||||||
.ifPresent(entry -> {
|
|
||||||
entry.setTaken(true);
|
entry.setTaken(true);
|
||||||
teleportationTarget.copyFrom(entry.entity);
|
teleportationTarget.copyFrom(entry.entity);
|
||||||
|
targetPortalId = entry.getSpellId();
|
||||||
setDirty();
|
setDirty();
|
||||||
});
|
}
|
||||||
}
|
return false;
|
||||||
|
});
|
||||||
private Optional<Ether.Entry> getTarget(Caster<?> source) {
|
|
||||||
return teleportationTarget.getTarget().flatMap(target -> Ether.get(source.asWorld()).getEntry(getType(), target.uuid()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -174,41 +211,40 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
LivingEntity caster = source.getMaster();
|
LivingEntity caster = source.getMaster();
|
||||||
Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos());
|
Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos());
|
||||||
parent.setOrientation(pitch, yaw);
|
parent.setOrientation(pitch, yaw);
|
||||||
entity.setPos(targetPos.x, caster.getY() + 1.5, targetPos.z);
|
entity.setPos(targetPos.x, Math.abs(pitch) > 15 ? targetPos.y : caster.getPos().y, 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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroyed(Caster<?> caster) {
|
protected void onDestroyed(Caster<?> caster) {
|
||||||
particleEffect.destroy();
|
|
||||||
Ether ether = Ether.get(caster.asWorld());
|
Ether ether = Ether.get(caster.asWorld());
|
||||||
ether.remove(getType(), caster.asEntity().getUuid());
|
ether.remove(getType(), caster);
|
||||||
getTarget(caster).ifPresent(e -> e.setTaken(false));
|
getTarget(caster).ifPresent(e -> e.setTaken(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
super.toNBT(compound);
|
super.toNBT(compound);
|
||||||
|
if (targetPortalId != null) {
|
||||||
|
compound.putUuid("targetPortalId", targetPortalId);
|
||||||
|
}
|
||||||
compound.putBoolean("publishedPosition", publishedPosition);
|
compound.putBoolean("publishedPosition", publishedPosition);
|
||||||
compound.put("teleportationTarget", teleportationTarget.toNBT());
|
compound.put("teleportationTarget", teleportationTarget.toNBT());
|
||||||
compound.putFloat("pitch", pitch);
|
compound.putFloat("pitch", pitch);
|
||||||
compound.putFloat("yaw", yaw);
|
compound.putFloat("yaw", yaw);
|
||||||
|
compound.putFloat("targetPortalPitch", targetPortalPitch);
|
||||||
|
compound.putFloat("targetPortalYaw", targetPortalYaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(compound);
|
super.fromNBT(compound);
|
||||||
|
targetPortalId = compound.containsUuid("targetPortalId") ? compound.getUuid("targetPortalId") : null;
|
||||||
publishedPosition = compound.getBoolean("publishedPosition");
|
publishedPosition = compound.getBoolean("publishedPosition");
|
||||||
teleportationTarget.fromNBT(compound.getCompound("teleportationTarget"));
|
teleportationTarget.fromNBT(compound.getCompound("teleportationTarget"));
|
||||||
pitch = compound.getFloat("pitch");
|
pitch = compound.getFloat("pitch");
|
||||||
yaw = compound.getFloat("yaw");
|
yaw = compound.getFloat("yaw");
|
||||||
|
targetPortalPitch = compound.getFloat("targetPortalPitch");
|
||||||
|
targetPortalYaw = compound.getFloat("targetPortalYaw");
|
||||||
particleArea = PARTICLE_AREA.rotate(
|
particleArea = PARTICLE_AREA.rotate(
|
||||||
pitch * MathHelper.RADIANS_PER_DEGREE,
|
pitch * MathHelper.RADIANS_PER_DEGREE,
|
||||||
(180 - yaw) * MathHelper.RADIANS_PER_DEGREE
|
(180 - yaw) * MathHelper.RADIANS_PER_DEGREE
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.Affinity;
|
import com.minelittlepony.unicopia.Affinity;
|
||||||
import com.minelittlepony.unicopia.USounds;
|
import com.minelittlepony.unicopia.USounds;
|
||||||
import com.minelittlepony.unicopia.Unicopia;
|
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.SpellTraits;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
|
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle;
|
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||||
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
|
|
||||||
import com.minelittlepony.unicopia.particle.UParticles;
|
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
|
|
||||||
import com.minelittlepony.unicopia.projectile.ProjectileUtil;
|
import com.minelittlepony.unicopia.projectile.ProjectileUtil;
|
||||||
import com.minelittlepony.unicopia.util.shape.Sphere;
|
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.player.PlayerEntity;
|
||||||
import net.minecraft.entity.vehicle.AbstractMinecartEntity;
|
import net.minecraft.entity.vehicle.AbstractMinecartEntity;
|
||||||
import net.minecraft.entity.vehicle.BoatEntity;
|
import net.minecraft.entity.vehicle.BoatEntity;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
public class ShieldSpell extends AbstractSpell {
|
public class ShieldSpell extends AbstractSpell {
|
||||||
|
@ -40,9 +41,16 @@ public class ShieldSpell extends AbstractSpell {
|
||||||
.with(Trait.AIR, 9)
|
.with(Trait.AIR, 9)
|
||||||
.build();
|
.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) {
|
protected ShieldSpell(CustomisedSpellType<?> type) {
|
||||||
super(type);
|
super(type);
|
||||||
|
@ -53,33 +61,27 @@ public class ShieldSpell extends AbstractSpell {
|
||||||
return method == CastingMethod.STAFF || getTraits().get(Trait.GENEROSITY) > 0 ? toPlaceable() : this;
|
return method == CastingMethod.STAFF || getTraits().get(Trait.GENEROSITY) > 0 ? toPlaceable() : this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroyed(Caster<?> caster) {
|
|
||||||
particlEffect.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Affinity getAffinity() {
|
public Affinity getAffinity() {
|
||||||
return getTraits().get(Trait.DARKNESS) > 0 ? Affinity.BAD : Affinity.GOOD;
|
return getTraits().get(Trait.DARKNESS) > 0 ? Affinity.BAD : Affinity.GOOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void generateParticles(Caster<?> source) {
|
protected void generateParticles(Caster<?> source) {
|
||||||
float radius = (float)getDrawDropOffRange(source);
|
|
||||||
Vec3d origin = getOrigin(source);
|
Vec3d origin = getOrigin(source);
|
||||||
|
|
||||||
source.spawnParticles(origin, new Sphere(true, radius), (int)(radius * 6), pos -> {
|
source.spawnParticles(origin, new Sphere(true, radius), (int)(radius * 6), pos -> {
|
||||||
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
|
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
|
||||||
});
|
|
||||||
|
|
||||||
particlEffect.update(getUuid(), source, spawner -> {
|
if (source.asWorld().random.nextInt(10) == 0 && source.asWorld().random.nextFloat() < source.getCorruption().getScaled(1)) {
|
||||||
spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, getType().getColor(), 0.3F, radius), origin, Vec3d.ZERO);
|
ParticleUtils.spawnParticle(source.asWorld(), new LightningBoltParticleEffect(true, 3, 2, 0.1F, Optional.empty()), pos, Vec3d.ZERO);
|
||||||
}).ifPresent(p -> {
|
}
|
||||||
p.setAttribute(Attachment.ATTR_RADIUS, radius);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
|
prevRadius = radius;
|
||||||
|
radius = (float)getDrawDropOffRange(source);
|
||||||
|
|
||||||
if (source.isClient()) {
|
if (source.isClient()) {
|
||||||
generateParticles(source);
|
generateParticles(source);
|
||||||
|
@ -97,27 +99,53 @@ public class ShieldSpell extends AbstractSpell {
|
||||||
|
|
||||||
long costMultiplier = applyEntities(source);
|
long costMultiplier = applyEntities(source);
|
||||||
if (costMultiplier > 0) {
|
if (costMultiplier > 0) {
|
||||||
double cost = 2 - source.getLevel().getScaled(2);
|
consumeManage(source, costMultiplier, knowledge);
|
||||||
|
|
||||||
cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F);
|
|
||||||
cost /= knowledge;
|
|
||||||
cost += getDrawDropOffRange(source) / 10F;
|
|
||||||
|
|
||||||
if (!source.subtractEnergyCost(cost)) {
|
|
||||||
setDead();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isDead();
|
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.
|
* Calculates the maximum radius of the shield. aka The area of effect.
|
||||||
*/
|
*/
|
||||||
public double getDrawDropOffRange(Caster<?> source) {
|
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);
|
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;
|
return range;
|
||||||
}
|
}
|
||||||
|
@ -146,11 +174,8 @@ public class ShieldSpell extends AbstractSpell {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected long applyEntities(Caster<?> source) {
|
protected long applyEntities(Caster<?> source) {
|
||||||
double radius = getDrawDropOffRange(source);
|
|
||||||
|
|
||||||
Vec3d origin = getOrigin(source);
|
Vec3d origin = getOrigin(source);
|
||||||
|
targetSelecter.getEntities(source, radius).forEach(i -> {
|
||||||
targetSelecter.getEntities(source, radius, this::isValidTarget).forEach(i -> {
|
|
||||||
try {
|
try {
|
||||||
applyRadialEffect(source, i, i.getPos().distanceTo(origin), radius);
|
applyRadialEffect(source, i, i.getPos().distanceTo(origin), radius);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|
|
@ -10,28 +10,32 @@ import java.util.stream.Stream;
|
||||||
import com.minelittlepony.unicopia.EquinePredicates;
|
import com.minelittlepony.unicopia.EquinePredicates;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Affine;
|
import com.minelittlepony.unicopia.ability.magic.Affine;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
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.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.entity.Entity;
|
||||||
|
import net.minecraft.predicate.entity.EntityPredicates;
|
||||||
|
|
||||||
public class TargetSelecter {
|
public class TargetSelecter {
|
||||||
|
|
||||||
private final Map<UUID, Target> targets = new TreeMap<>();
|
private final Map<UUID, Target> targets = new TreeMap<>();
|
||||||
|
|
||||||
private final Spell spell;
|
private final Spell spell;
|
||||||
|
|
||||||
|
private BiPredicate<Caster<?>, Entity> filter = (a, b) -> true;
|
||||||
|
|
||||||
public TargetSelecter(Spell spell) {
|
public TargetSelecter(Spell spell) {
|
||||||
this.spell = spell;
|
this.spell = spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<Entity> getEntities(Caster<?> source, double radius, BiPredicate<Caster<?>, Entity> filter) {
|
public TargetSelecter setFilter(BiPredicate<Caster<?>, Entity> filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<Entity> getEntities(Caster<?> source, double radius) {
|
||||||
targets.values().removeIf(Target::tick);
|
targets.values().removeIf(Target::tick);
|
||||||
return source.findAllEntitiesInRange(radius)
|
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(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 -> {
|
.map(i -> {
|
||||||
targets.computeIfAbsent(i.getUuid(), Target::new);
|
targets.computeIfAbsent(i.getUuid(), Target::new);
|
||||||
return i;
|
return i;
|
||||||
|
@ -42,30 +46,19 @@ public class TargetSelecter {
|
||||||
return targets.values().stream().filter(Target::canHurt).count();
|
return targets.values().stream().filter(Target::canHurt).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends Entity> Predicate<T> notOwnerOrFriend(Affine affine, Caster<?> source) {
|
public static <T extends Entity> Predicate<T> validTarget(Affine affine, Caster<?> source) {
|
||||||
return target -> notOwnerOrFriend(affine, source, target);
|
return target -> validTarget(affine, source, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends Entity> Predicate<T> isOwnerOrFriend(Affine affine, Caster<?> source) {
|
public static boolean validTarget(Affine affine, Caster<?> source, Entity target) {
|
||||||
return target -> isOwnerOrFriend(affine, source, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends Entity> boolean notOwnerOrFriend(Affine affine, Caster<?> source, Entity target) {
|
|
||||||
return !isOwnerOrFriend(affine, source, target);
|
return !isOwnerOrFriend(affine, source, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends Entity> boolean isOwnerOrFriend(Affine affine, Caster<?> source, Entity target) {
|
public static boolean isOwnerOrFriend(Affine affine, Caster<?> source, Entity target) {
|
||||||
Entity owner = source.getMaster();
|
return affine.applyInversion(source, source.isOwnerOrFriend(target));
|
||||||
|
|
||||||
if (affine.isEnemy(source)) {
|
|
||||||
return FriendshipBraceletItem.isComrade(source, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FriendshipBraceletItem.isComrade(source, target)
|
|
||||||
|| (owner != null && (Pony.equal(target, owner) || owner.isConnectedThroughVehicle(target)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class Target {
|
private static final class Target {
|
||||||
private int cooldown = 20;
|
private int cooldown = 20;
|
||||||
|
|
||||||
Target(UUID id) { }
|
Target(UUID id) { }
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.minelittlepony.unicopia.ability.magic.spell.trait;
|
||||||
|
|
||||||
|
public interface ItemWithTraits {
|
||||||
|
SpellTraits getDefaultTraits();
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import net.fabricmc.api.Environment;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.inventory.Inventory;
|
import net.minecraft.inventory.Inventory;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
|
import net.minecraft.item.SpawnEggItem;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.nbt.NbtElement;
|
import net.minecraft.nbt.NbtElement;
|
||||||
|
@ -41,6 +42,7 @@ import net.minecraft.registry.Registries;
|
||||||
|
|
||||||
public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
|
public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
|
||||||
public static final SpellTraits EMPTY = new SpellTraits(Map.of());
|
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<Identifier, SpellTraits> REGISTRY = new HashMap<>();
|
private static Map<Identifier, SpellTraits> REGISTRY = new HashMap<>();
|
||||||
static final Map<Trait, List<Item>> ITEMS = new HashMap<>();
|
static final Map<Trait, List<Item>> ITEMS = new HashMap<>();
|
||||||
|
@ -220,6 +222,12 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpellTraits of(Item item) {
|
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);
|
return REGISTRY.getOrDefault(Registries.ITEM.getId(item), EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import net.minecraft.util.math.*;
|
||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import net.minecraft.world.*;
|
import net.minecraft.world.*;
|
||||||
|
|
||||||
public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock {
|
public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock, ZapStagedBlock {
|
||||||
|
|
||||||
BaseZapAppleLeavesBlock() {
|
BaseZapAppleLeavesBlock() {
|
||||||
super(Settings.create()
|
super(Settings.create()
|
||||||
|
@ -29,61 +29,29 @@ public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasRandomTicks(BlockState state) {
|
public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) {
|
||||||
return !state.get(PERSISTENT);
|
if (state.get(PERSISTENT) || oldState.isOf(state.getBlock())) {
|
||||||
}
|
return;
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
updateStage(state, world, pos);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||||
super.scheduledTick(state, world, pos, 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)) {
|
if (state.get(PERSISTENT)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
tryAdvanceStage(state, world, pos, random);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ZapAppleStageStore.Stage getStage(BlockState state) {
|
@Override
|
||||||
|
public ZapAppleStageStore.Stage getStage(BlockState state) {
|
||||||
return ZapAppleStageStore.Stage.FLOWERING;
|
return ZapAppleStageStore.Stage.FLOWERING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldDecay(BlockState state) {
|
protected final boolean shouldDecay(BlockState state) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,40 +82,10 @@ public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getTint(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int foliageColor) {
|
public int getTint(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int foliageColor) {
|
||||||
|
|
||||||
if (pos == null) {
|
if (pos == null) {
|
||||||
return 0x4C7EFA;
|
return 0x4C7EFA;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TintedBlock.blend(TintedBlock.rotate(foliageColor, 2), 0x0000FF, 0.3F);
|
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
229
src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java
Normal file
229
src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java
Normal file
|
@ -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<EdibleBlock> 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<BlockState, VoxelShape> 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<Block, BlockState> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Block, BlockState> builder) {
|
||||||
|
super.appendProperties(builder);
|
||||||
|
builder.add(ENCHANTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -62,6 +62,14 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean shouldAdvance(Random random) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BlockState getPlacedFruitState(Random random) {
|
||||||
|
return fruit.get().getDefaultState();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||||
super.randomTick(state, world, pos, random);
|
super.randomTick(state, world, pos, random);
|
||||||
|
@ -70,10 +78,14 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (world.isDay()) {
|
if (world.getBaseLightLevel(pos, 0) > 8) {
|
||||||
BlockSoundGroup group = getSoundGroup(state);
|
BlockSoundGroup group = getSoundGroup(state);
|
||||||
int steps = FertilizableUtil.getGrowthSteps(world, pos, state, random);
|
int steps = FertilizableUtil.getGrowthSteps(world, pos, state, random);
|
||||||
while (steps-- > 0) {
|
while (steps-- > 0) {
|
||||||
|
if (!shouldAdvance(random)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (state.get(STAGE) == Stage.FRUITING) {
|
if (state.get(STAGE) == Stage.FRUITING) {
|
||||||
state = state.cycle(AGE);
|
state = state.cycle(AGE);
|
||||||
if (state.get(AGE) > 20) {
|
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 (stage == Stage.FRUITING && isPositionValidForFruit(state, pos)) {
|
||||||
if (world.isAir(fruitPosition)) {
|
if (world.isAir(fruitPosition)) {
|
||||||
world.setBlockState(fruitPosition, fruit.get().getDefaultState(), Block.NOTIFY_ALL);
|
world.setBlockState(fruitPosition, getPlacedFruitState(random), Block.NOTIFY_ALL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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<Block> fruit,
|
||||||
|
Supplier<ItemStack> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import com.minelittlepony.unicopia.block.cloud.SoggyCloudBlock;
|
||||||
import com.minelittlepony.unicopia.block.cloud.SoggyCloudSlabBlock;
|
import com.minelittlepony.unicopia.block.cloud.SoggyCloudSlabBlock;
|
||||||
import com.minelittlepony.unicopia.block.cloud.SoggyCloudStairsBlock;
|
import com.minelittlepony.unicopia.block.cloud.SoggyCloudStairsBlock;
|
||||||
import com.minelittlepony.unicopia.block.cloud.UnstableCloudBlock;
|
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.UItems;
|
||||||
import com.minelittlepony.unicopia.item.cloud.CloudBlockItem;
|
import com.minelittlepony.unicopia.item.cloud.CloudBlockItem;
|
||||||
import com.minelittlepony.unicopia.item.group.ItemGroupRegistry;
|
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.object.builder.v1.block.FabricBlockSettings;
|
||||||
import net.fabricmc.fabric.api.registry.FlammableBlockRegistry;
|
import net.fabricmc.fabric.api.registry.FlammableBlockRegistry;
|
||||||
import net.fabricmc.fabric.api.registry.StrippableBlockRegistry;
|
import net.fabricmc.fabric.api.registry.StrippableBlockRegistry;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.block.*;
|
import net.minecraft.block.*;
|
||||||
import net.minecraft.block.AbstractBlock.Settings;
|
import net.minecraft.block.AbstractBlock.Settings;
|
||||||
import net.minecraft.block.entity.BlockEntityType;
|
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_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_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 = 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_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_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);
|
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 = 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 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),
|
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_SLICE,
|
||||||
() -> UItems.APPLE_PIE,
|
() -> 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 = register("plunder_vine", new ThornBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).ticksRandomly().sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY), () -> UBlocks.PLUNDER_VINE_BUD));
|
||||||
Block PLUNDER_VINE_BUD = register("plunder_vine_bud", new ThornBudBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).nonOpaque().ticksRandomly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY), PLUNDER_VINE.getDefaultState()));
|
Block 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 CHITIN = register("chitin", new SnowyBlock(Settings.create().mapColor(MapColor.PALE_PURPLE).hardness(5).requiresTool().ticksRandomly().sounds(BlockSoundGroup.CORAL)), ItemGroups.NATURAL);
|
||||||
Block SURFACE_CHITIN = register("surface_chitin", new GrowableBlock(Settings.copy(CHITIN), () -> CHITIN), ItemGroups.NATURAL);
|
Block 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 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);
|
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 extends Block> T register(String name, T item) {
|
private static <T extends Block> T register(String name, T item) {
|
||||||
return register(Unicopia.id(name), item);
|
return register(Unicopia.id(name), item);
|
||||||
}
|
}
|
||||||
|
@ -229,10 +250,15 @@ public interface UBlocks {
|
||||||
if (block instanceof CloudLike || block instanceof SlimePustuleBlock || block instanceof PileBlock) {
|
if (block instanceof CloudLike || block instanceof SlimePustuleBlock || block instanceof PileBlock) {
|
||||||
SEMI_TRANSPARENT_BLOCKS.add(block);
|
SEMI_TRANSPARENT_BLOCKS.add(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Registry.register(Registries.BLOCK, id, block);
|
return Registry.register(Registries.BLOCK, id, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bootstrap() {
|
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.SIGN).addSupportedBlocks(PALM_SIGN, PALM_WALL_SIGN);
|
||||||
BlockEntityTypeSupportHelper.of(BlockEntityType.HANGING_SIGN).addSupportedBlocks(PALM_HANGING_SIGN, PALM_WALL_HANGING_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(PALM_LOG, STRIPPED_PALM_LOG);
|
||||||
StrippableBlockRegistry.register(ZAP_WOOD, STRIPPED_ZAP_WOOD);
|
StrippableBlockRegistry.register(ZAP_WOOD, STRIPPED_ZAP_WOOD);
|
||||||
StrippableBlockRegistry.register(PALM_WOOD, STRIPPED_PALM_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);
|
TintedBlock.REGISTRY.add(PALM_LEAVES);
|
||||||
|
|
||||||
FlammableBlockRegistry.getDefaultInstance().add(GREEN_APPLE_LEAVES, 30, 60);
|
FlammableBlockRegistry.getDefaultInstance().add(GREEN_APPLE_LEAVES, 30, 60);
|
||||||
FlammableBlockRegistry.getDefaultInstance().add(SWEET_APPLE_LEAVES, 30, 60);
|
FlammableBlockRegistry.getDefaultInstance().add(SWEET_APPLE_LEAVES, 30, 60);
|
||||||
FlammableBlockRegistry.getDefaultInstance().add(SOUR_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(MANGO_LEAVES, 30, 60);
|
||||||
FlammableBlockRegistry.getDefaultInstance().add(PALM_LEAVES, 30, 60);
|
FlammableBlockRegistry.getDefaultInstance().add(PALM_LEAVES, 30, 60);
|
||||||
FlammableBlockRegistry.getDefaultInstance().add(PALM_LOG, 5, 5);
|
FlammableBlockRegistry.getDefaultInstance().add(PALM_LOG, 5, 5);
|
||||||
FlammableBlockRegistry.getDefaultInstance().add(PALM_WOOD, 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_LOG, 5, 5);
|
||||||
FlammableBlockRegistry.getDefaultInstance().add(STRIPPED_PALM_WOOD, 5, 5);
|
FlammableBlockRegistry.getDefaultInstance().add(STRIPPED_PALM_WOOD, 5, 5);
|
||||||
FlammableBlockRegistry.getDefaultInstance().add(PALM_PLANKS, 5, 20);
|
FlammableBlockRegistry.getDefaultInstance().add(PALM_PLANKS, 5, 20);
|
||||||
FlammableBlockRegistry.getDefaultInstance().add(BANANAS, 5, 20);
|
FlammableBlockRegistry.getDefaultInstance().add(BANANAS, 5, 20);
|
||||||
|
|
||||||
UBlockEntities.bootstrap();
|
UBlockEntities.bootstrap();
|
||||||
|
EdibleBlock.bootstrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
public interface UWoodTypes {
|
public interface UWoodTypes {
|
||||||
WoodType PALM = register("palm");
|
WoodType PALM = register("palm");
|
||||||
|
WoodType GOLDEN_OAK = register("golden_oak");
|
||||||
RegistryKey<TerraformBoatType> PALM_BOAT_TYPE = TerraformBoatTypeRegistry.createKey(Unicopia.id("palm"));
|
RegistryKey<TerraformBoatType> PALM_BOAT_TYPE = TerraformBoatTypeRegistry.createKey(Unicopia.id("palm"));
|
||||||
|
|
||||||
BlockSetType CLOUD = new BlockSetTypeBuilder()
|
BlockSetType CLOUD = new BlockSetTypeBuilder()
|
||||||
|
|
|
@ -4,11 +4,8 @@ import com.minelittlepony.unicopia.server.world.ZapAppleStageStore;
|
||||||
|
|
||||||
import net.minecraft.block.*;
|
import net.minecraft.block.*;
|
||||||
import net.minecraft.item.ItemPlacementContext;
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
|
||||||
import net.minecraft.state.StateManager;
|
import net.minecraft.state.StateManager;
|
||||||
import net.minecraft.state.property.*;
|
import net.minecraft.state.property.*;
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
|
|
||||||
public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock {
|
public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock {
|
||||||
public static final EnumProperty<ZapAppleStageStore.Stage> STAGE = EnumProperty.of("stage", ZapAppleStageStore.Stage.class);
|
public static final EnumProperty<ZapAppleStageStore.Stage> STAGE = EnumProperty.of("stage", ZapAppleStageStore.Stage.class);
|
||||||
|
@ -17,31 +14,13 @@ public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock {
|
||||||
setDefaultState(getDefaultState().with(STAGE, ZapAppleStageStore.Stage.GREENING));
|
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
|
@Override
|
||||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||||
return super.getPlacementState(ctx).with(STAGE, ZapAppleStageStore.Stage.GREENING);
|
return super.getPlacementState(ctx).with(STAGE, ZapAppleStageStore.Stage.GREENING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ZapAppleStageStore.Stage getStage(BlockState state) {
|
public ZapAppleStageStore.Stage getStage(BlockState state) {
|
||||||
return state.get(STAGE);
|
return state.get(STAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,51 +1,34 @@
|
||||||
package com.minelittlepony.unicopia.block;
|
package com.minelittlepony.unicopia.block;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.server.world.ZapAppleStageStore;
|
import com.minelittlepony.unicopia.server.world.ZapAppleStageStore;
|
||||||
|
import com.minelittlepony.unicopia.server.world.ZapAppleStageStore.Stage;
|
||||||
|
|
||||||
import net.minecraft.block.*;
|
import net.minecraft.block.*;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.math.*;
|
import net.minecraft.util.math.*;
|
||||||
import net.minecraft.util.math.random.Random;
|
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() {
|
ZapAppleLeavesPlaceholderBlock() {
|
||||||
super(Settings.create().replaceable().noCollision().dropsNothing().air());
|
super(Settings.create().replaceable().noCollision().dropsNothing().air());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasRandomTicks(BlockState state) {
|
public Stage getStage(BlockState state) {
|
||||||
return true;
|
return ZapAppleStageStore.Stage.HIBERNATING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
|
public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) {
|
||||||
|
updateStage(state, world, pos);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||||
super.scheduledTick(state, world, pos, random);
|
super.scheduledTick(state, world, pos, random);
|
||||||
|
tryAdvanceStage(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
@ -22,7 +24,7 @@ public class PoreousCloudBlock extends CloudBlock implements Soakable {
|
||||||
@Override
|
@Override
|
||||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||||
if (moisture <= 0) {
|
if (moisture <= 0) {
|
||||||
return Soakable.copyProperties(state, getDefaultState());
|
return StateUtil.copyState(state, getDefaultState());
|
||||||
}
|
}
|
||||||
return soggyBlock == null ? null : soggyBlock.get().getStateWithMoisture(state, moisture);
|
return soggyBlock == null ? null : soggyBlock.get().getStateWithMoisture(state, moisture);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
|
||||||
public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakable {
|
public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakable {
|
||||||
|
@ -19,7 +21,7 @@ public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakabl
|
||||||
@Override
|
@Override
|
||||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||||
if (moisture <= 0) {
|
if (moisture <= 0) {
|
||||||
return Soakable.copyProperties(state, getDefaultState());
|
return StateUtil.copyState(state, getDefaultState());
|
||||||
}
|
}
|
||||||
return soggyBlock.get().getStateWithMoisture(state, moisture);
|
return soggyBlock.get().getStateWithMoisture(state, moisture);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.sound.SoundCategory;
|
import net.minecraft.sound.SoundCategory;
|
||||||
import net.minecraft.sound.SoundEvents;
|
import net.minecraft.sound.SoundEvents;
|
||||||
import net.minecraft.state.property.IntProperty;
|
import net.minecraft.state.property.IntProperty;
|
||||||
import net.minecraft.state.property.Property;
|
|
||||||
import net.minecraft.util.ActionResult;
|
import net.minecraft.util.ActionResult;
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.Util;
|
import net.minecraft.util.Util;
|
||||||
|
@ -105,15 +104,4 @@ public interface Soakable {
|
||||||
world.setBlockState(pos, soakable.getStateWithMoisture(state, newMoisture));
|
world.setBlockState(pos, soakable.getStateWithMoisture(state, newMoisture));
|
||||||
world.playSound(null, pos, SoundEvents.ENTITY_SALMON_FLOP, SoundCategory.BLOCKS, 1, (float)world.random.nextTriangular(0.5, 0.3F));
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
@ -43,9 +45,9 @@ public class SoggyCloudBlock extends CloudBlock implements Soakable {
|
||||||
@Override
|
@Override
|
||||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||||
if (moisture <= 0) {
|
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
|
@Override
|
||||||
|
|
|
@ -4,6 +4,8 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
@ -43,9 +45,9 @@ public class SoggyCloudSlabBlock extends CloudSlabBlock {
|
||||||
@Override
|
@Override
|
||||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||||
if (moisture <= 0) {
|
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
|
@Override
|
||||||
|
|
|
@ -4,6 +4,8 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
@ -36,8 +38,8 @@ public class SoggyCloudStairsBlock extends CloudStairsBlock implements Soakable
|
||||||
@Override
|
@Override
|
||||||
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
public BlockState getStateWithMoisture(BlockState state, int moisture) {
|
||||||
if (moisture <= 0) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import com.minelittlepony.unicopia.Unicopia;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.state.property.Property;
|
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.JsonHelper;
|
import net.minecraft.util.JsonHelper;
|
||||||
import net.minecraft.registry.Registries;
|
import net.minecraft.registry.Registries;
|
||||||
|
@ -50,7 +49,7 @@ public abstract class StateChange {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
return Registries.BLOCK.getOrEmpty(id).map(Block::getDefaultState)
|
return Registries.BLOCK.getOrEmpty(id).map(Block::getDefaultState)
|
||||||
.map(newState -> merge(newState, state))
|
.map(newState -> StateUtil.copyState(state, newState))
|
||||||
.orElse(state);
|
.orElse(state);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -101,17 +100,4 @@ public abstract class StateChange {
|
||||||
return serializer.apply(json);
|
return serializer.apply(json);
|
||||||
}).orElseThrow(() -> new IllegalArgumentException("Invalid action " + action));
|
}).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 <T extends Comparable<T>> BlockState copy(BlockState to, BlockState from, Property<T> property) {
|
|
||||||
return to.with(property, from.get(property));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ package com.minelittlepony.unicopia.client;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.UUID;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@ -13,19 +13,20 @@ import com.minelittlepony.unicopia.EquinePredicates;
|
||||||
import com.minelittlepony.unicopia.FlightType;
|
import com.minelittlepony.unicopia.FlightType;
|
||||||
import com.minelittlepony.unicopia.InteractionManager;
|
import com.minelittlepony.unicopia.InteractionManager;
|
||||||
import com.minelittlepony.unicopia.USounds;
|
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.DismissSpellScreen;
|
||||||
import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters;
|
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.client.sound.*;
|
||||||
import com.minelittlepony.unicopia.entity.player.PlayerPhysics;
|
import com.minelittlepony.unicopia.entity.player.PlayerPhysics;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity;
|
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 com.mojang.authlib.GameProfile;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.network.ClientPlayerEntity;
|
||||||
import net.minecraft.client.sound.AggressiveBeeSoundInstance;
|
import net.minecraft.client.sound.AggressiveBeeSoundInstance;
|
||||||
import net.minecraft.client.sound.MovingMinecartSoundInstance;
|
import net.minecraft.client.sound.MovingMinecartSoundInstance;
|
||||||
import net.minecraft.client.sound.PassiveBeeSoundInstance;
|
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.player.PlayerEntity;
|
||||||
import net.minecraft.entity.vehicle.AbstractMinecartEntity;
|
import net.minecraft.entity.vehicle.AbstractMinecartEntity;
|
||||||
import net.minecraft.network.PacketByteBuf;
|
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.sound.SoundCategory;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import net.minecraft.world.BlockView;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class ClientInteractionManager extends InteractionManager {
|
public class ClientInteractionManager extends InteractionManager {
|
||||||
|
|
||||||
private final MinecraftClient client = MinecraftClient.getInstance();
|
private final MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
|
||||||
private final Optional<CasterView> clientWorld = Optional.of(() -> MinecraftClient.getInstance().world);
|
|
||||||
|
|
||||||
private final Int2ObjectMap<WeakReference<TickableSoundInstance>> playingSounds = new Int2ObjectOpenHashMap<>();
|
private final Int2ObjectMap<WeakReference<TickableSoundInstance>> playingSounds = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<CasterView> getCasterView(BlockView view) {
|
|
||||||
if (view instanceof ServerWorld world) {
|
|
||||||
return Optional.of(Ether.get(world));
|
|
||||||
}
|
|
||||||
return clientWorld;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Identifier, ?> readChapters(PacketByteBuf buffer) {
|
public Map<Identifier, ?> readChapters(PacketByteBuf buffer) {
|
||||||
return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter);
|
return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -159,4 +149,16 @@ public class ClientInteractionManager extends InteractionManager {
|
||||||
public int getViewMode() {
|
public int getViewMode() {
|
||||||
return client.options.getPerspective().ordinal();
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import com.minelittlepony.unicopia.block.cloud.CloudChestBlock;
|
||||||
import com.minelittlepony.unicopia.client.particle.ChangelingMagicParticle;
|
import com.minelittlepony.unicopia.client.particle.ChangelingMagicParticle;
|
||||||
import com.minelittlepony.unicopia.client.particle.CloudsEscapingParticle;
|
import com.minelittlepony.unicopia.client.particle.CloudsEscapingParticle;
|
||||||
import com.minelittlepony.unicopia.client.particle.DiskParticle;
|
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.GroundPoundParticle;
|
||||||
import com.minelittlepony.unicopia.client.particle.HealthDrainParticle;
|
import com.minelittlepony.unicopia.client.particle.HealthDrainParticle;
|
||||||
import com.minelittlepony.unicopia.client.particle.LightningBoltParticle;
|
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.particle.SphereParticle;
|
||||||
import com.minelittlepony.unicopia.client.render.*;
|
import com.minelittlepony.unicopia.client.render.*;
|
||||||
import com.minelittlepony.unicopia.client.render.entity.*;
|
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.client.render.spell.SpellRendererFactory;
|
||||||
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||||
import com.minelittlepony.unicopia.item.ChameleonItem;
|
import com.minelittlepony.unicopia.item.ChameleonItem;
|
||||||
|
@ -59,12 +62,14 @@ import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public interface URenderers {
|
public interface URenderers {
|
||||||
BlockEntity CHEST_RENDER_ENTITY = new CloudChestBlock.TileData(BlockPos.ORIGIN, UBlocks.CLOUD_CHEST.getDefaultState());
|
BlockEntity CHEST_RENDER_ENTITY = new CloudChestBlock.TileData(BlockPos.ORIGIN, UBlocks.CLOUD_CHEST.getDefaultState());
|
||||||
|
|
||||||
static void bootstrap() {
|
static void bootstrap() {
|
||||||
ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new));
|
ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new));
|
||||||
ParticleFactoryRegistry.getInstance().register(UParticles.CHANGELING_MAGIC, createFactory(ChangelingMagicParticle::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.RAIN_DROPS, createFactory(RaindropsParticle::new));
|
||||||
ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create));
|
ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create));
|
||||||
ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new);
|
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.GROUND_POUND, GroundPoundParticle::new);
|
||||||
ParticleFactoryRegistry.getInstance().register(UParticles.CLOUDS_ESCAPING, CloudsEscapingParticle::new);
|
ParticleFactoryRegistry.getInstance().register(UParticles.CLOUDS_ESCAPING, CloudsEscapingParticle::new);
|
||||||
ParticleFactoryRegistry.getInstance().register(UParticles.LIGHTNING_BOLT, LightningBoltParticle::new);
|
ParticleFactoryRegistry.getInstance().register(UParticles.LIGHTNING_BOLT, LightningBoltParticle::new);
|
||||||
|
ParticleFactoryRegistry.getInstance().register(UParticles.DUST_CLOUD, DustCloudParticle::new);
|
||||||
|
|
||||||
AccessoryFeatureRenderer.register(
|
AccessoryFeatureRenderer.register(
|
||||||
BraceletFeatureRenderer::new, AmuletFeatureRenderer::new, GlassesFeatureRenderer::new,
|
BraceletFeatureRenderer::new, AmuletFeatureRenderer::new, GlassesFeatureRenderer::new,
|
||||||
|
@ -97,6 +103,8 @@ public interface URenderers {
|
||||||
EntityRendererRegistry.register(UEntities.AIR_BALLOON, AirBalloonEntityRenderer::new);
|
EntityRendererRegistry.register(UEntities.AIR_BALLOON, AirBalloonEntityRenderer::new);
|
||||||
EntityRendererRegistry.register(UEntities.FRIENDLY_CREEPER, FriendlyCreeperEntityRenderer::new);
|
EntityRendererRegistry.register(UEntities.FRIENDLY_CREEPER, FriendlyCreeperEntityRenderer::new);
|
||||||
EntityRendererRegistry.register(UEntities.LOOT_BUG, LootBugEntityRenderer::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.WEATHER_VANE, WeatherVaneBlockEntityRenderer::new);
|
||||||
BlockEntityRendererFactories.register(UBlockEntities.FANCY_BED, CloudBedBlockEntityRenderer::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);
|
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.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(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.BLOCK.register(URenderers::getTintedBlockColor, TintedBlock.REGISTRY.stream().toArray(Block[]::new));
|
||||||
ColorProviderRegistry.ITEM.register((stack, i) -> getTintedBlockColor(Block.getBlockFromItem(stack.getItem()).getDefaultState(), null, null, i), TintedBlock.REGISTRY.stream().map(Block::asItem).filter(i -> i != Items.AIR).toArray(Item[]::new));
|
ColorProviderRegistry.ITEM.register((stack, i) -> 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);
|
TerraformBoatClientHelper.registerModelLayers(Unicopia.id("palm"), false);
|
||||||
|
|
||||||
SpellRendererFactory.bootstrap();
|
SpellRendererFactory.bootstrap();
|
||||||
|
UShaders.bootstrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void register(DynamicItemRenderer renderer, ItemConvertible...items) {
|
private static void register(DynamicItemRenderer renderer, ItemConvertible...items) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import com.minelittlepony.unicopia.entity.player.PlayerCamera;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.network.handler.ClientNetworkHandlerImpl;
|
import com.minelittlepony.unicopia.network.handler.ClientNetworkHandlerImpl;
|
||||||
import com.minelittlepony.unicopia.server.world.WeatherConditions;
|
import com.minelittlepony.unicopia.server.world.WeatherConditions;
|
||||||
|
import com.minelittlepony.unicopia.server.world.ZapAppleStageStore;
|
||||||
import com.minelittlepony.unicopia.util.Lerp;
|
import com.minelittlepony.unicopia.util.Lerp;
|
||||||
|
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
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 tangentalSkyAngle = new Lerp(0, true);
|
||||||
public final Lerp skyAngle = 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<PlayerCamera> getCamera() {
|
public static Optional<PlayerCamera> getCamera() {
|
||||||
PlayerEntity player = MinecraftClient.getInstance().player;
|
PlayerEntity player = MinecraftClient.getInstance().player;
|
||||||
|
|
||||||
|
@ -84,6 +88,15 @@ public class UnicopiaClient implements ClientModInitializer {
|
||||||
instance = this;
|
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) {
|
public float getSkyAngleDelta(float tickDelta) {
|
||||||
if (MinecraftClient.getInstance().world == null) {
|
if (MinecraftClient.getInstance().world == null) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -135,6 +148,8 @@ public class UnicopiaClient implements ClientModInitializer {
|
||||||
world.setRainGradient(gradient);
|
world.setRainGradient(gradient);
|
||||||
world.setThunderGradient(gradient);
|
world.setThunderGradient(gradient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zapStageTime++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Float getTargetRainGradient(ClientWorld world, BlockPos pos, float tickDelta) {
|
private Float getTargetRainGradient(ClientWorld world, BlockPos pos, float tickDelta) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.client.gui;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2IntFunction;
|
import it.unimi.dsi.fastutil.ints.Int2IntFunction;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.font.TextHandler;
|
import net.minecraft.client.font.TextHandler;
|
||||||
|
@ -31,12 +30,18 @@ public class ParagraphWrappingVisitor implements StyledVisitor<Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Object> accept(Style style, String s) {
|
public Optional<Object> accept(Style initialStyle, String s) {
|
||||||
|
StyleBreakingVisitor visitor = new StyleBreakingVisitor(initialStyle, this::acceptFragment);
|
||||||
|
TextVisitFactory.visitFormatted(s, 0, initialStyle, initialStyle, visitor);
|
||||||
|
visitor.flush();
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Object> acceptFragment(Style inlineStyle, String s) {
|
||||||
int remainingLength = (int)(pageWidth - currentLineCollectedLength);
|
int remainingLength = (int)(pageWidth - currentLineCollectedLength);
|
||||||
|
|
||||||
while (!s.isEmpty()) {
|
while (!s.isEmpty()) {
|
||||||
int trimmedLength = handler.getTrimmedLength(s, remainingLength, style);
|
int trimmedLength = handler.getTrimmedLength(s, remainingLength, inlineStyle);
|
||||||
int newline = s.indexOf('\n');
|
int newline = s.indexOf('\n');
|
||||||
|
|
||||||
if (newline >= 0 && newline < trimmedLength) {
|
if (newline >= 0 && newline < trimmedLength) {
|
||||||
|
@ -57,7 +62,7 @@ public class ParagraphWrappingVisitor implements StyledVisitor<Object> {
|
||||||
trimmedLength = lastSpace > 0 ? Math.min(lastSpace, trimmedLength) : trimmedLength;
|
trimmedLength = lastSpace > 0 ? Math.min(lastSpace, trimmedLength) : trimmedLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
Text fragment = Text.literal(s.substring(0, trimmedLength).trim()).setStyle(style);
|
Text fragment = Text.literal(s.substring(0, trimmedLength).trim()).setStyle(inlineStyle);
|
||||||
float grabbedWidth = handler.getWidth(fragment);
|
float grabbedWidth = handler.getWidth(fragment);
|
||||||
|
|
||||||
// advance if appending the next segment would cause an overflow
|
// advance if appending the next segment would cause an overflow
|
||||||
|
@ -101,14 +106,42 @@ public class ParagraphWrappingVisitor implements StyledVisitor<Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void advance() {
|
public void advance() {
|
||||||
|
line++;
|
||||||
if (progressedNonEmpty || currentLineCollectedLength > 0) {
|
if (progressedNonEmpty || currentLineCollectedLength > 0) {
|
||||||
progressedNonEmpty = true;
|
progressedNonEmpty = true;
|
||||||
lineConsumer.accept(currentLine, (++line) * font.fontHeight);
|
lineConsumer.accept(currentLine, line * font.fontHeight);
|
||||||
}
|
}
|
||||||
pageWidth = widthSupplier.applyAsInt((++line) * font.fontHeight);
|
pageWidth = widthSupplier.applyAsInt(line * font.fontHeight);
|
||||||
currentLine = Text.empty();
|
currentLine = Text.empty();
|
||||||
currentLineCollectedLength = 0;
|
currentLineCollectedLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
record StyledString (String string, Style style) {}
|
static final class StyleBreakingVisitor implements CharacterVisitor {
|
||||||
|
private final StyledVisitor<?> fragmentVisitor;
|
||||||
|
private final StringBuilder collectedText = new StringBuilder();
|
||||||
|
|
||||||
|
private Style currentStyle;
|
||||||
|
|
||||||
|
public StyleBreakingVisitor(Style initialStyle, StyledVisitor<?> fragmentVisitor) {
|
||||||
|
this.currentStyle = initialStyle;
|
||||||
|
this.fragmentVisitor = fragmentVisitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(int index, Style style, int codepoint) {
|
||||||
|
if (!style.equals(currentStyle)) {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
currentStyle = style;
|
||||||
|
collectedText.append((char)codepoint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush() {
|
||||||
|
if (collectedText.length() > 0) {
|
||||||
|
fragmentVisitor.accept(currentStyle, collectedText.toString());
|
||||||
|
collectedText.setLength(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,6 +7,8 @@ import com.minelittlepony.unicopia.*;
|
||||||
import com.minelittlepony.unicopia.ability.*;
|
import com.minelittlepony.unicopia.ability.*;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
|
import com.minelittlepony.unicopia.client.render.RenderLayers;
|
||||||
|
import com.minelittlepony.unicopia.client.render.spell.DarkVortexSpellRenderer;
|
||||||
import com.minelittlepony.unicopia.client.sound.*;
|
import com.minelittlepony.unicopia.client.sound.*;
|
||||||
import com.minelittlepony.unicopia.entity.ItemTracker;
|
import com.minelittlepony.unicopia.entity.ItemTracker;
|
||||||
import com.minelittlepony.unicopia.entity.effect.EffectUtils;
|
import com.minelittlepony.unicopia.entity.effect.EffectUtils;
|
||||||
|
@ -193,6 +195,19 @@ public class UHud {
|
||||||
|
|
||||||
protected void renderViewEffects(Pony pony, DrawContext context, int scaledWidth, int scaledHeight, float tickDelta) {
|
protected void renderViewEffects(Pony pony, DrawContext context, int scaledWidth, int scaledHeight, float tickDelta) {
|
||||||
|
|
||||||
|
float vortexDistortion = DarkVortexSpellRenderer.getCameraDistortion();
|
||||||
|
|
||||||
|
if (vortexDistortion > 20) {
|
||||||
|
context.fill(RenderLayers.getEndPortal(), 0, 0, scaledWidth, scaledHeight, 0);
|
||||||
|
context.getMatrices().push();
|
||||||
|
context.getMatrices().translate(scaledWidth / 2, scaledHeight / 2, 0);
|
||||||
|
DrawableUtil.drawArc(context.getMatrices(), 0, 20, 0, MathHelper.TAU, 0x000000FF, false);
|
||||||
|
context.getMatrices().pop();
|
||||||
|
return;
|
||||||
|
} else if (vortexDistortion > 0) {
|
||||||
|
context.fill(0, 0, scaledWidth, scaledHeight, (int)((vortexDistortion / 20F) * 255) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
boolean hasEffect = client.player.hasStatusEffect(UEffects.SUN_BLINDNESS);
|
boolean hasEffect = client.player.hasStatusEffect(UEffects.SUN_BLINDNESS);
|
||||||
|
|
||||||
ItemStack glasses = GlassesItem.getForEntity(client.player);
|
ItemStack glasses = GlassesItem.getForEntity(client.player);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.minelittlepony.common.client.gui.ScrollContainer;
|
||||||
import com.minelittlepony.common.client.gui.element.Label;
|
import com.minelittlepony.common.client.gui.element.Label;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
|
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
|
||||||
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
|
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
|
||||||
|
import com.minelittlepony.unicopia.client.gui.MagicText;
|
||||||
import com.minelittlepony.unicopia.container.SpellbookState;
|
import com.minelittlepony.unicopia.container.SpellbookState;
|
||||||
import com.minelittlepony.unicopia.item.URecipes;
|
import com.minelittlepony.unicopia.item.URecipes;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
@ -43,13 +44,10 @@ public class SpellbookCraftingPageContent extends ScrollContainer implements Spe
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) {
|
public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) {
|
||||||
|
DrawableUtil.drawScaledText(context, state.getOffset() == 0 ? INVENTORY_TITLE : RECIPES_TITLE, screen.getFrameBounds().left + screen.getFrameBounds().width / 2 + 20, SpellbookScreen.TITLE_Y, 1.3F, MagicText.getColor());
|
||||||
int headerColor = mouseY % 255;
|
|
||||||
|
|
||||||
DrawableUtil.drawScaledText(context, state.getOffset() == 0 ? INVENTORY_TITLE : RECIPES_TITLE, screen.getFrameBounds().left + screen.getFrameBounds().width / 2 + 20, SpellbookScreen.TITLE_Y, 1.3F, headerColor);
|
|
||||||
|
|
||||||
Text pageText = Text.translatable("%s/%s", state.getOffset() + 1, TOTAL_PAGES);
|
Text pageText = Text.translatable("%s/%s", state.getOffset() + 1, TOTAL_PAGES);
|
||||||
context.drawText(textRenderer, pageText, (int)(337 - textRenderer.getWidth(pageText) / 2F), 190, headerColor, false);
|
context.drawText(textRenderer, pageText, (int)(337 - textRenderer.getWidth(pageText) / 2F), 190, MagicText.getColor(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class SpellbookProfilePageContent implements SpellbookChapterList.Content
|
||||||
float currentScaledLevel = pony.getLevel().getScaled(1);
|
float currentScaledLevel = pony.getLevel().getScaled(1);
|
||||||
float currentCorruption = pony.getCorruption().getScaled(1);
|
float currentCorruption = pony.getCorruption().getScaled(1);
|
||||||
|
|
||||||
DrawableUtil.drawScaledText(context, pony.asEntity().getName(), SpellbookScreen.TITLE_X, y, 1.3F, SpellbookScreen.TITLE_COLOR);
|
DrawableUtil.drawScaledText(context, pony.asEntity().getName(), SpellbookScreen.TITLE_X, y, 1.3F, MagicText.getColor());
|
||||||
DrawableUtil.drawScaledText(context, ExperienceGroup.forLevel(
|
DrawableUtil.drawScaledText(context, ExperienceGroup.forLevel(
|
||||||
currentScaledLevel,
|
currentScaledLevel,
|
||||||
currentCorruption
|
currentCorruption
|
||||||
|
@ -89,7 +89,7 @@ public class SpellbookProfilePageContent implements SpellbookChapterList.Content
|
||||||
matrices.push();
|
matrices.push();
|
||||||
matrices.translate(screen.getBackgroundWidth() / 2 + SpellbookScreen.TITLE_X - 10, y, 0);
|
matrices.translate(screen.getBackgroundWidth() / 2 + SpellbookScreen.TITLE_X - 10, y, 0);
|
||||||
matrices.scale(1.3F, 1.3F, 1);
|
matrices.scale(1.3F, 1.3F, 1);
|
||||||
context.drawText(font, SpellbookCraftingPageContent.INVENTORY_TITLE, 0, 0, SpellbookScreen.TITLE_COLOR, false);
|
context.drawText(font, SpellbookCraftingPageContent.INVENTORY_TITLE, 0, 0, MagicText.getColor(), false);
|
||||||
matrices.pop();
|
matrices.pop();
|
||||||
|
|
||||||
Bounds bounds = screen.getFrameBounds();
|
Bounds bounds = screen.getFrameBounds();
|
||||||
|
|
|
@ -159,7 +159,6 @@ public class SpellbookScreen extends HandledScreen<SpellbookScreenHandler> imple
|
||||||
|
|
||||||
tabs.getAllTabs().forEach(tab -> {
|
tabs.getAllTabs().forEach(tab -> {
|
||||||
Bounds bounds = tab.bounds();
|
Bounds bounds = tab.bounds();
|
||||||
chapters.getCurrentChapter();
|
|
||||||
boolean hover = bounds.contains(mouseX, mouseY);
|
boolean hover = bounds.contains(mouseX, mouseY);
|
||||||
|
|
||||||
int color = tab.chapter().color() & 0xFFFFFF;
|
int color = tab.chapter().color() & 0xFFFFFF;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import java.util.*;
|
||||||
import com.minelittlepony.common.client.gui.IViewRoot;
|
import com.minelittlepony.common.client.gui.IViewRoot;
|
||||||
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
||||||
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
|
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
|
||||||
|
import com.minelittlepony.unicopia.client.gui.MagicText;
|
||||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
|
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
|
||||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Content;
|
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Content;
|
||||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Drawable;
|
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Drawable;
|
||||||
|
@ -12,7 +13,6 @@ import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow;
|
||||||
import com.minelittlepony.unicopia.container.SpellbookState;
|
import com.minelittlepony.unicopia.container.SpellbookState;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.font.TextRenderer;
|
|
||||||
import net.minecraft.client.gui.DrawContext;
|
import net.minecraft.client.gui.DrawContext;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import net.minecraft.network.PacketByteBuf;
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
@ -21,13 +21,15 @@ import net.minecraft.util.*;
|
||||||
|
|
||||||
public class DynamicContent implements Content {
|
public class DynamicContent implements Content {
|
||||||
private static final Text UNKNOWN = Text.of("???");
|
private static final Text UNKNOWN = Text.of("???");
|
||||||
private static final Text UNKNOWN_LEVEL = Text.literal("Level: ???").formatted(Formatting.DARK_GREEN);
|
|
||||||
|
|
||||||
private SpellbookState.PageState state = new SpellbookState.PageState();
|
private SpellbookState.PageState state = new SpellbookState.PageState();
|
||||||
private final List<Page> pages;
|
private final List<Page> pages;
|
||||||
|
|
||||||
private Bounds bounds = Bounds.empty();
|
private Bounds bounds = Bounds.empty();
|
||||||
|
|
||||||
|
private final Panel leftPanel = new Panel(this);
|
||||||
|
private final Panel rightPanel = new Panel(this);
|
||||||
|
|
||||||
public DynamicContent(PacketByteBuf buffer) {
|
public DynamicContent(PacketByteBuf buffer) {
|
||||||
pages = buffer.readList(Page::new);
|
pages = buffer.readList(Page::new);
|
||||||
}
|
}
|
||||||
|
@ -35,32 +37,25 @@ public class DynamicContent implements Content {
|
||||||
@Override
|
@Override
|
||||||
public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) {
|
public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) {
|
||||||
int pageIndex = state.getOffset() * 2;
|
int pageIndex = state.getOffset() * 2;
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
|
||||||
getPage(pageIndex).ifPresent(page -> page.draw(context, mouseX, mouseY, container));
|
Text pageText = Text.translatable("%s/%s", (pageIndex / 2) + 1, (int)Math.ceil(pages.size() / 2F));
|
||||||
|
context.drawText(client.textRenderer, pageText, (int)(337 - client.textRenderer.getWidth(pageText) / 2F), 190, MagicText.getColor(), false);
|
||||||
context.getMatrices().push();
|
|
||||||
getPage(pageIndex + 1).ifPresent(page -> {
|
|
||||||
page.bounds.left = bounds.left + bounds.width / 2 + 20;
|
|
||||||
page.draw(context, mouseX, mouseY, container);
|
|
||||||
});
|
|
||||||
context.getMatrices().pop();
|
|
||||||
|
|
||||||
TextRenderer font = MinecraftClient.getInstance().textRenderer;
|
|
||||||
int headerColor = mouseY % 255;
|
|
||||||
|
|
||||||
Text pageText = Text.translatable("%s/%s", (pageIndex / 2) + 1, pages.size() / 2);
|
|
||||||
context.drawText(font, pageText, (int)(337 - font.getWidth(pageText) / 2F), 190, headerColor, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copyStateFrom(Content old) {
|
public void copyStateFrom(Content old) {
|
||||||
if (old instanceof DynamicContent o) {
|
if (old instanceof DynamicContent o) {
|
||||||
|
if (state.getOffset() == o.state.getOffset()) {
|
||||||
|
leftPanel.verticalScrollbar.getScrubber().scrollTo(o.leftPanel.verticalScrollbar.getScrubber().getPosition(), false);
|
||||||
|
rightPanel.verticalScrollbar.getScrubber().scrollTo(o.rightPanel.verticalScrollbar.getScrubber().getPosition(), false);
|
||||||
|
}
|
||||||
state = o.state;
|
state = o.state;
|
||||||
setBounds(o.bounds);
|
setBounds(o.bounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Page> getPage(int index) {
|
Optional<Page> getPage(int index) {
|
||||||
if (index < 0 || index >= pages.size()) {
|
if (index < 0 || index >= pages.size()) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
@ -71,9 +66,15 @@ public class DynamicContent implements Content {
|
||||||
this.bounds = bounds;
|
this.bounds = bounds;
|
||||||
pages.forEach(page -> {
|
pages.forEach(page -> {
|
||||||
page.reset();
|
page.reset();
|
||||||
|
int oldHeight = page.bounds.height;
|
||||||
page.bounds.copy(bounds);
|
page.bounds.copy(bounds);
|
||||||
|
page.bounds.left = 0;
|
||||||
|
page.bounds.top = 0;
|
||||||
page.bounds.width /= 2;
|
page.bounds.width /= 2;
|
||||||
|
page.bounds.height = oldHeight;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
leftPanel.setBounds(bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -83,6 +84,10 @@ public class DynamicContent implements Content {
|
||||||
screen.addPageButtons(187, 30, 350, incr -> {
|
screen.addPageButtons(187, 30, 350, incr -> {
|
||||||
state.swap(incr, (int)Math.ceil(pages.size() / 2F));
|
state.swap(incr, (int)Math.ceil(pages.size() / 2F));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
int pageIndex = state.getOffset() * 2;
|
||||||
|
leftPanel.init(screen, pageIndex);
|
||||||
|
rightPanel.init(screen, pageIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Page implements Drawable {
|
class Page implements Drawable {
|
||||||
|
@ -131,6 +136,19 @@ public class DynamicContent implements Content {
|
||||||
compiled = false;
|
compiled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void drawHeader(DrawContext context, int mouseX, int mouseY) {
|
||||||
|
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level;
|
||||||
|
int x = bounds.left;
|
||||||
|
int y = bounds.top - 16;
|
||||||
|
|
||||||
|
DrawableUtil.drawScaledText(context, needsMoreXp ? UNKNOWN : title, x, y, 1.3F, MagicText.getColor());
|
||||||
|
DrawableUtil.drawScaledText(context, Text.translatable("gui.unicopia.spellbook.page.level_requirement", level < 0 ? "???" : "" + (level + 1)).formatted(Formatting.DARK_GREEN), x, y + 12, 0.8F, MagicText.getColor());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) {
|
public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) {
|
||||||
|
|
||||||
|
@ -141,26 +159,24 @@ public class DynamicContent implements Content {
|
||||||
if (!compiled) {
|
if (!compiled) {
|
||||||
compiled = true;
|
compiled = true;
|
||||||
int relativeY = 0;
|
int relativeY = 0;
|
||||||
|
int textHeight = 0;
|
||||||
for (PageElement element : elements.stream().filter(PageElement::isInline).toList()) {
|
for (PageElement element : elements.stream().filter(PageElement::isInline).toList()) {
|
||||||
element.compile(relativeY, container);
|
element.compile(relativeY, container);
|
||||||
relativeY += element.bounds().height;
|
relativeY += element.bounds().height;
|
||||||
|
if (element instanceof TextBlock) {
|
||||||
|
textHeight += element.bounds().height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
bounds.height = textHeight == 0 ? 0 : relativeY;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level;
|
|
||||||
|
|
||||||
int headerColor = mouseY % 255;
|
|
||||||
|
|
||||||
MatrixStack matrices = context.getMatrices();
|
MatrixStack matrices = context.getMatrices();
|
||||||
DrawableUtil.drawScaledText(context, needsMoreXp ? UNKNOWN : title, bounds.left, bounds.top - 10, 1.3F, headerColor);
|
|
||||||
DrawableUtil.drawScaledText(context, level < 0 ? UNKNOWN_LEVEL : Text.literal("Level: " + (level + 1)).formatted(Formatting.DARK_GREEN), bounds.left, bounds.top - 10 + 12, 0.8F, headerColor);
|
|
||||||
|
|
||||||
matrices.push();
|
matrices.push();
|
||||||
matrices.translate(bounds.left, bounds.top + 16, 0);
|
matrices.translate(0, -8, 0);
|
||||||
elements.stream().filter(PageElement::isFloating).forEach(element -> {
|
elements.stream().filter(PageElement::isFloating).forEach(element -> {
|
||||||
Bounds bounds = element.bounds();
|
|
||||||
matrices.push();
|
matrices.push();
|
||||||
bounds.translate(matrices);
|
element.bounds().translate(matrices);
|
||||||
element.draw(context, mouseX, mouseY, container);
|
element.draw(context, mouseX, mouseY, container);
|
||||||
matrices.pop();
|
matrices.pop();
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.minelittlepony.unicopia.client.gui.spellbook.element;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import com.minelittlepony.common.client.gui.IViewRoot;
|
||||||
|
import com.minelittlepony.common.client.gui.ScrollContainer;
|
||||||
|
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
||||||
|
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
|
||||||
|
import com.minelittlepony.unicopia.client.gui.spellbook.element.DynamicContent.Page;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
|
||||||
|
class Panel extends ScrollContainer {
|
||||||
|
private final DynamicContent content;
|
||||||
|
|
||||||
|
Panel(DynamicContent content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Page> page = Optional.empty();
|
||||||
|
|
||||||
|
public void init(SpellbookScreen screen, int pageIndex) {
|
||||||
|
verticalScrollbar.layoutToEnd = true;
|
||||||
|
getContentPadding().top = 15;
|
||||||
|
page = content.getPage(pageIndex);
|
||||||
|
|
||||||
|
int width = screen.getBackgroundWidth() / 2;
|
||||||
|
margin.top = screen.getY() + 35;
|
||||||
|
margin.bottom = screen.height - screen.getBackgroundHeight() - screen.getY() + 40;
|
||||||
|
margin.left = screen.getX() + 30;
|
||||||
|
|
||||||
|
if (pageIndex % 2 == 1) {
|
||||||
|
margin.left += screen.getBackgroundWidth() / 2 - 20;
|
||||||
|
margin.right = screen.getX() + 20;
|
||||||
|
} else {
|
||||||
|
margin.right = screen.width - width - margin.left + 30;
|
||||||
|
}
|
||||||
|
init(() -> {});
|
||||||
|
screen.addDrawable(this);
|
||||||
|
((IViewRoot)screen).getChildElements().add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderContents(DrawContext context, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
page.ifPresent(p -> {
|
||||||
|
int oldHeight = p.getBounds().height;
|
||||||
|
p.draw(context, mouseX, mouseY, this);
|
||||||
|
if (p.getBounds().height != oldHeight) {
|
||||||
|
verticalScrollbar.reposition();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
super.renderContents(context, mouseX, mouseY, partialTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bounds getContentBounds() {
|
||||||
|
return page == null ? Bounds.empty() : page.map(page -> {
|
||||||
|
return new Bounds(0, 0, 1, page.getBounds().height);
|
||||||
|
}).orElse(Bounds.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawBackground(DrawContext context, int mouseX, int mouseY, float partialTicks) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawDecorations(DrawContext context, int mouseX, int mouseY, float partialTicks) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawOverlays(DrawContext context, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
context.getMatrices().push();
|
||||||
|
this.getBounds().translate(context.getMatrices());
|
||||||
|
page.ifPresent(p -> {
|
||||||
|
p.drawHeader(context, mouseX, mouseY);
|
||||||
|
});
|
||||||
|
context.getMatrices().pop();
|
||||||
|
super.drawOverlays(context, mouseX, mouseY, partialTicks);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import com.minelittlepony.common.client.gui.IViewRoot;
|
||||||
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
|
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
|
||||||
import com.minelittlepony.unicopia.client.gui.spellbook.IngredientTree;
|
import com.minelittlepony.unicopia.client.gui.spellbook.IngredientTree;
|
||||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
|
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
@ -14,18 +13,14 @@ import net.minecraft.util.Identifier;
|
||||||
record Recipe (DynamicContent.Page page, Identifier id, Bounds bounds) implements PageElement {
|
record Recipe (DynamicContent.Page page, Identifier id, Bounds bounds) implements PageElement {
|
||||||
@Override
|
@Override
|
||||||
public void compile(int y, IViewRoot container) {
|
public void compile(int y, IViewRoot container) {
|
||||||
if (container instanceof SpellbookScreen book) {
|
|
||||||
bounds().left = book.getX();
|
|
||||||
bounds().top = book.getY();
|
|
||||||
}
|
|
||||||
MinecraftClient.getInstance().world.getRecipeManager().get(id).map(RecipeEntry::value).ifPresent(recipe -> {
|
MinecraftClient.getInstance().world.getRecipeManager().get(id).map(RecipeEntry::value).ifPresent(recipe -> {
|
||||||
if (recipe instanceof SpellbookRecipe spellRecipe) {
|
if (recipe instanceof SpellbookRecipe spellRecipe) {
|
||||||
|
|
||||||
boolean needsMoreXp = page.getLevel() < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < page.getLevel();
|
boolean needsMoreXp = page.getLevel() < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < page.getLevel();
|
||||||
|
|
||||||
IngredientTree tree = new IngredientTree(
|
IngredientTree tree = new IngredientTree(
|
||||||
bounds().left + page().getBounds().left,
|
bounds().left,
|
||||||
bounds().top + page().getBounds().top + y + 10,
|
bounds().top + y - 10,
|
||||||
page().getBounds().width - 20
|
page().getBounds().width - 20
|
||||||
).obfuscateResult(needsMoreXp);
|
).obfuscateResult(needsMoreXp);
|
||||||
spellRecipe.buildCraftingTree(tree);
|
spellRecipe.buildCraftingTree(tree);
|
||||||
|
|
|
@ -4,19 +4,13 @@ import com.minelittlepony.common.client.gui.IViewRoot;
|
||||||
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.crafting.IngredientWithSpell;
|
import com.minelittlepony.unicopia.ability.magic.spell.crafting.IngredientWithSpell;
|
||||||
import com.minelittlepony.unicopia.client.gui.spellbook.IngredientTree;
|
import com.minelittlepony.unicopia.client.gui.spellbook.IngredientTree;
|
||||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
|
|
||||||
|
|
||||||
record Stack (DynamicContent.Page page, IngredientWithSpell ingredient, Bounds bounds) implements PageElement {
|
record Stack (DynamicContent.Page page, IngredientWithSpell ingredient, Bounds bounds) implements PageElement {
|
||||||
@Override
|
@Override
|
||||||
public void compile(int y, IViewRoot container) {
|
public void compile(int y, IViewRoot container) {
|
||||||
int xx = 0, yy = 0;
|
|
||||||
if (container instanceof SpellbookScreen book) {
|
|
||||||
xx = book.getX();
|
|
||||||
yy = book.getY();
|
|
||||||
}
|
|
||||||
IngredientTree tree = new IngredientTree(
|
IngredientTree tree = new IngredientTree(
|
||||||
bounds().left + xx + page().getBounds().left,
|
bounds().left + page().getBounds().left,
|
||||||
bounds().top + yy + page().getBounds().top + y + 10,
|
bounds().top + page().getBounds().top + y - 10,
|
||||||
30
|
30
|
||||||
);
|
);
|
||||||
tree.input(ingredient.getMatchingStacks());
|
tree.input(ingredient.getMatchingStacks());
|
||||||
|
|
|
@ -6,6 +6,7 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import com.minelittlepony.common.client.gui.IViewRoot;
|
import com.minelittlepony.common.client.gui.IViewRoot;
|
||||||
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
||||||
|
import com.minelittlepony.unicopia.client.gui.MagicText;
|
||||||
import com.minelittlepony.unicopia.client.gui.ParagraphWrappingVisitor;
|
import com.minelittlepony.unicopia.client.gui.ParagraphWrappingVisitor;
|
||||||
import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow;
|
import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
|
@ -52,7 +53,7 @@ class TextBlock implements PageElement {
|
||||||
MatrixStack matrices = context.getMatrices();
|
MatrixStack matrices = context.getMatrices();
|
||||||
matrices.push();
|
matrices.push();
|
||||||
wrappedText.forEach(line -> {
|
wrappedText.forEach(line -> {
|
||||||
context.drawText(font, needsMoreXp ? line.text().copy().formatted(Formatting.OBFUSCATED) : line.text().copy(), line.x(), 0, 0, false);
|
context.drawText(font, needsMoreXp ? line.text().copy().formatted(Formatting.OBFUSCATED) : line.text().copy(), line.x(), 0, MagicText.getColor(), false);
|
||||||
matrices.translate(0, font.fontHeight, 0);
|
matrices.translate(0, font.fontHeight, 0);
|
||||||
});
|
});
|
||||||
matrices.pop();
|
matrices.pop();
|
||||||
|
|
|
@ -2,6 +2,8 @@ package com.minelittlepony.unicopia.client.particle;
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.client.render.RenderUtil;
|
||||||
|
|
||||||
import net.minecraft.client.particle.Particle;
|
import net.minecraft.client.particle.Particle;
|
||||||
import net.minecraft.client.particle.ParticleTextureSheet;
|
import net.minecraft.client.particle.ParticleTextureSheet;
|
||||||
import net.minecraft.client.render.BufferBuilder;
|
import net.minecraft.client.render.BufferBuilder;
|
||||||
|
@ -9,6 +11,7 @@ import net.minecraft.client.render.Tessellator;
|
||||||
import net.minecraft.client.render.VertexConsumer;
|
import net.minecraft.client.render.VertexConsumer;
|
||||||
import net.minecraft.client.render.VertexFormat;
|
import net.minecraft.client.render.VertexFormat;
|
||||||
import net.minecraft.client.render.VertexFormats;
|
import net.minecraft.client.render.VertexFormats;
|
||||||
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
|
||||||
public abstract class AbstractGeometryBasedParticle extends Particle {
|
public abstract class AbstractGeometryBasedParticle extends Particle {
|
||||||
|
@ -36,6 +39,25 @@ public abstract class AbstractGeometryBasedParticle extends Particle {
|
||||||
te.draw();
|
te.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final void renderQuad(MatrixStack matrices, Tessellator te, BufferBuilder buffer, RenderUtil.Vertex[] corners, float alpha, float tickDelta) {
|
||||||
|
int light = getBrightness(tickDelta);
|
||||||
|
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
|
||||||
|
for (RenderUtil.Vertex corner : corners) {
|
||||||
|
var position = corner.position(matrices.peek().getPositionMatrix());
|
||||||
|
buffer.vertex(position.x, position.y, position.z).texture(corner.texture().x, corner.texture().y).color(red, green, blue, alpha).light(light).next();
|
||||||
|
}
|
||||||
|
te.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void renderQuad(Tessellator te, BufferBuilder buffer, RenderUtil.Vertex[] corners, float alpha, float tickDelta) {
|
||||||
|
int light = getBrightness(tickDelta);
|
||||||
|
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
|
||||||
|
for (RenderUtil.Vertex corner : corners) {
|
||||||
|
buffer.vertex(corner.position().x, corner.position().y, corner.position().z).texture(corner.texture().x, corner.texture().y).color(red, green, blue, alpha).light(light).next();
|
||||||
|
}
|
||||||
|
te.draw();
|
||||||
|
}
|
||||||
|
|
||||||
protected final void renderQuad(VertexConsumer buffer, Vector3f[] corners, float alpha, float tickDelta) {
|
protected final void renderQuad(VertexConsumer buffer, Vector3f[] corners, float alpha, float tickDelta) {
|
||||||
int light = getBrightness(tickDelta);
|
int light = getBrightness(tickDelta);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.minelittlepony.unicopia.client.particle;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.particle.ParticleSpawner;
|
||||||
|
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.particle.Particle;
|
||||||
|
import net.minecraft.particle.ParticleEffect;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A connection class for updating and persisting an attached particle effect.
|
||||||
|
*/
|
||||||
|
public class ClientBoundParticleSpawner implements ParticleSpawner {
|
||||||
|
private static final Map<UUID, Entry> SPAWNED_PARTICLES = new HashMap<>();
|
||||||
|
|
||||||
|
private final UUID id;
|
||||||
|
private WeakReference<BooleanSupplier> attachment = new WeakReference<>(null);
|
||||||
|
|
||||||
|
private final MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
|
||||||
|
public ClientBoundParticleSpawner(UUID id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addParticle(ParticleEffect effect, Vec3d pos, Vec3d vel) {
|
||||||
|
BooleanSupplier a = attachment.get();
|
||||||
|
if ((a == null || !a.getAsBoolean())) {
|
||||||
|
SPAWNED_PARTICLES.values().removeIf(set -> !set.getAsBoolean());
|
||||||
|
attachment = new WeakReference<>(SPAWNED_PARTICLES.computeIfAbsent(id, i -> {
|
||||||
|
return new Entry(
|
||||||
|
new WeakReference<>(client.world),
|
||||||
|
new WeakReference<>(client.particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z))
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Entry (WeakReference<World> world, WeakReference<Particle> particle) implements BooleanSupplier {
|
||||||
|
@Override
|
||||||
|
public boolean getAsBoolean() {
|
||||||
|
if (world.get() == null || world.get() != MinecraftClient.getInstance().world) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Particle particle = this.particle.get();
|
||||||
|
return particle != null && particle.isAlive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ public class CloudsEscapingParticle extends GroundPoundParticle {
|
||||||
);
|
);
|
||||||
|
|
||||||
double columnHeight = 1 + age / 30;
|
double columnHeight = 1 + age / 30;
|
||||||
new Sphere(true, columnHeight, 1, 1, 1)
|
new Sphere(true, columnHeight)
|
||||||
.translate(center)
|
.translate(center)
|
||||||
.randomPoints(random)
|
.randomPoints(random)
|
||||||
.forEach(point -> {
|
.forEach(point -> {
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.minelittlepony.unicopia.client.particle;
|
||||||
|
|
||||||
|
import com.minelittlepony.common.util.Color;
|
||||||
|
import com.minelittlepony.unicopia.client.render.RenderUtil;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.render.BufferBuilder;
|
||||||
|
import net.minecraft.client.render.Tessellator;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
import net.minecraft.particle.BlockStateParticleEffect;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
import net.minecraft.util.math.RotationAxis;
|
||||||
|
|
||||||
|
public class DustCloudParticle extends AbstractBillboardParticle {
|
||||||
|
//private static final Identifier TEXTURE = new Identifier("textures/particle/big_smoke_3.png");
|
||||||
|
|
||||||
|
protected static final int SEGMENTS = 20;
|
||||||
|
protected static final int SEPARATION = 270 / SEGMENTS;
|
||||||
|
|
||||||
|
private float scaleFactor;
|
||||||
|
|
||||||
|
protected Sprite sprite;
|
||||||
|
private final RenderUtil.Vertex[] vertices;
|
||||||
|
|
||||||
|
public DustCloudParticle(BlockStateParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
|
||||||
|
super(world, x, y, z, velocityX, velocityY, velocityZ);
|
||||||
|
maxAge = 1000;
|
||||||
|
gravityStrength = 1;
|
||||||
|
red = 0.6F;
|
||||||
|
green = 0.6F;
|
||||||
|
blue = 0.6F;
|
||||||
|
alpha = (float)world.getRandom().nextTriangular(0.6, 0.2);
|
||||||
|
scaleFactor = (float)world.getRandom().nextTriangular(2, 1.2);
|
||||||
|
sprite = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelParticleSprite(effect.getBlockState());
|
||||||
|
vertices = new RenderUtil.Vertex[]{
|
||||||
|
new RenderUtil.Vertex(-1, -1, 0, sprite.getMinU(), sprite.getMinV()),
|
||||||
|
new RenderUtil.Vertex(-1, 1, 0, sprite.getMaxU(), sprite.getMinV()),
|
||||||
|
new RenderUtil.Vertex( 1, 1, 0, sprite.getMaxU(), sprite.getMaxV()),
|
||||||
|
new RenderUtil.Vertex( 1, -1, 0, sprite.getMinU(), sprite.getMaxV())
|
||||||
|
};
|
||||||
|
if (!effect.getBlockState().isOf(Blocks.GRASS_BLOCK)) {
|
||||||
|
int i = MinecraftClient.getInstance().getBlockColors().getColor(effect.getBlockState(), world, BlockPos.ofFloored(x, y, z), 0);
|
||||||
|
red *= Color.r(i);
|
||||||
|
green *= Color.g(i);
|
||||||
|
blue *= Color.b(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Identifier getTexture() {
|
||||||
|
return sprite.getAtlasId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
scaleFactor += 0.001F;
|
||||||
|
scale(MathHelper.clamp(age / 5F, 0, 1) * scaleFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) {
|
||||||
|
float scale = getScale(tickDelta);
|
||||||
|
float alpha = this.alpha * (1 - ((float)age / maxAge));
|
||||||
|
MatrixStack matrices = new MatrixStack();
|
||||||
|
matrices.translate(x, y, z);
|
||||||
|
matrices.scale(scale, scale * 0.5F, scale);
|
||||||
|
|
||||||
|
float angle = ((this.age + tickDelta) % 360) / SEGMENTS;
|
||||||
|
|
||||||
|
for (int i = 0; i < SEGMENTS; i++) {
|
||||||
|
matrices.push();
|
||||||
|
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees((i * angle) % 360));
|
||||||
|
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees((SEPARATION * i + angle) % 360));
|
||||||
|
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((SEPARATION * i + angle) % 360));
|
||||||
|
float ringScale = 1 + MathHelper.sin(((i * 10) + age + tickDelta) * 0.05F) * 0.1F;
|
||||||
|
matrices.scale(ringScale, ringScale, ringScale);
|
||||||
|
renderQuad(matrices, te, buffer, vertices, alpha, tickDelta);
|
||||||
|
matrices.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.minelittlepony.unicopia.client.particle;
|
||||||
|
|
||||||
|
import net.minecraft.client.particle.ParticleTextureSheet;
|
||||||
|
import net.minecraft.client.particle.SpriteBillboardParticle;
|
||||||
|
import net.minecraft.client.particle.SpriteProvider;
|
||||||
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
import net.minecraft.particle.ParticleEffect;
|
||||||
|
import net.minecraft.particle.ParticleTypes;
|
||||||
|
|
||||||
|
public class FloatingBubbleParticle extends SpriteBillboardParticle {
|
||||||
|
public FloatingBubbleParticle(ParticleEffect effect, SpriteProvider provider, ClientWorld clientWorld, double x, double y, double z, double dX, double dY, double dZ) {
|
||||||
|
super(clientWorld, x, y, z, dX, dY, dZ);
|
||||||
|
setSprite(provider);
|
||||||
|
scale((float)clientWorld.random.nextTriangular(1F, 0.5F));
|
||||||
|
this.velocityX *= -0.1F;
|
||||||
|
this.velocityY *= -0.1F;
|
||||||
|
this.velocityZ *= -0.1F;
|
||||||
|
this.maxAge *= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParticleTextureSheet getType() {
|
||||||
|
return ParticleTextureSheet.PARTICLE_SHEET_OPAQUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markDead() {
|
||||||
|
super.markDead();
|
||||||
|
world.addParticle(ParticleTypes.BUBBLE_POP, x, y, z, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,6 @@ public abstract class OrientedBillboardParticle extends AbstractBillboardParticl
|
||||||
|
|
||||||
fixed = effect.fixed();
|
fixed = effect.fixed();
|
||||||
if (fixed) {
|
if (fixed) {
|
||||||
// Was hamiltonianProduct (CHECK THIS!!)
|
|
||||||
rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(effect.pitch()));
|
rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(effect.pitch()));
|
||||||
rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - effect.yaw()));
|
rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - effect.yaw()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,46 @@
|
||||||
package com.minelittlepony.unicopia.client.particle;
|
package com.minelittlepony.unicopia.client.particle;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EntityConvertable;
|
|
||||||
import com.minelittlepony.unicopia.Unicopia;
|
import com.minelittlepony.unicopia.Unicopia;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.client.render.bezier.BezierSegment;
|
import com.minelittlepony.unicopia.client.render.bezier.BezierSegment;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.client.render.bezier.Trail;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
|
import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Link;
|
|
||||||
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.client.render.BufferBuilder;
|
import net.minecraft.client.render.BufferBuilder;
|
||||||
import net.minecraft.client.render.Tessellator;
|
import net.minecraft.client.render.Tessellator;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
import net.minecraft.particle.DefaultParticleType;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.Box;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
public class RainbowTrailParticle extends AbstractBillboardParticle implements Attachment {
|
public class RainbowTrailParticle extends AbstractBillboardParticle {
|
||||||
private static final Identifier TEXTURE = Unicopia.id("textures/particles/rainboom_trail.png");
|
private static final Identifier TEXTURE = Unicopia.id("textures/particles/rainboom_trail.png");
|
||||||
|
|
||||||
private final List<Segment> segments = new ArrayList<>();
|
private final Trail trail;
|
||||||
|
|
||||||
private Optional<Link> link = Optional.empty();
|
@Nullable
|
||||||
|
private Entity target;
|
||||||
|
private boolean isAbility;
|
||||||
|
|
||||||
private boolean bound;
|
public RainbowTrailParticle(TargetBoundParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
|
||||||
|
|
||||||
public RainbowTrailParticle(DefaultParticleType effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
|
|
||||||
super(world, x, y, z, velocityX, velocityY, velocityZ);
|
super(world, x, y, z, velocityX, velocityY, velocityZ);
|
||||||
segments.add(new Segment(new Vec3d(x, y, z)));
|
trail = new Trail(new Vec3d(x, y, z));
|
||||||
setMaxAge(300);
|
setMaxAge(300);
|
||||||
|
this.velocityX = velocityX;
|
||||||
|
this.velocityY = velocityY;
|
||||||
|
this.velocityZ = velocityZ;
|
||||||
|
|
||||||
|
if (effect.getTargetId() <= 0) {
|
||||||
|
this.target = world.getOtherEntities(null, Box.from(trail.pos)).get(0);
|
||||||
|
} else {
|
||||||
|
this.target = world.getEntityById(effect.getTargetId());
|
||||||
|
}
|
||||||
|
isAbility = Caster.of(target).filter(caster -> SpellType.RAINBOOM.isOn(caster)).isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -42,98 +49,45 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isStillAlive() {
|
public boolean isAlive() {
|
||||||
return age < getMaxAge() && (!dead || !segments.isEmpty());
|
return age < getMaxAge() && (!dead || !trail.getSegments().isEmpty());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attach(Link link) {
|
|
||||||
this.link = Optional.of(link);
|
|
||||||
bound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void detach() {
|
|
||||||
link = Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAttribute(int key, Number value) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) {
|
protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) {
|
||||||
float alpha = 1 - (float)age / maxAge;
|
float alpha = this.alpha * (1 - (float)age / maxAge);
|
||||||
|
|
||||||
|
List<Trail.Segment> segments = trail.getSegments();
|
||||||
|
|
||||||
for (int i = 0; i < segments.size() - 1; i++) {
|
for (int i = 0; i < segments.size() - 1; i++) {
|
||||||
BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1));
|
BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1));
|
||||||
float scale = getScale(tickDelta);
|
float scale = getScale(tickDelta);
|
||||||
|
|
||||||
corners.forEachCorner(corner -> {
|
corners.forEachCorner(corner -> {
|
||||||
corner.mul(scale);
|
corner.position().mul(scale).add(x, y, z);
|
||||||
corner.add(x, y, z);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
renderQuad(te, buffer, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta);
|
renderQuad(te, buffer, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void follow(EntityConvertable<?> caster) {
|
|
||||||
Vec3d next = caster.asEntity().getPos();
|
|
||||||
|
|
||||||
if (segments.isEmpty()) {
|
|
||||||
segments.add(new Segment(next));
|
|
||||||
} else {
|
|
||||||
Vec3d last = segments.get(segments.size() - 1).position;
|
|
||||||
if (next.distanceTo(last) > 0.2) {
|
|
||||||
segments.add(new Segment(next));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
super.tick();
|
super.tick();
|
||||||
|
|
||||||
if (link.isPresent()) {
|
if (target != null && target.isAlive()) {
|
||||||
age = 0;
|
if (isAbility) {
|
||||||
link.flatMap(Link::get).ifPresent(this::follow);
|
age = 0;
|
||||||
} else if (!dead && !bound) {
|
}
|
||||||
follow(Pony.of(MinecraftClient.getInstance().player));
|
trail.update(target.getEyePos());
|
||||||
|
|
||||||
|
if (isAbility && Caster.of(target).filter(caster -> SpellType.RAINBOOM.isOn(caster)).isEmpty()) {
|
||||||
|
target = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segments.size() > 1) {
|
if (trail.tick()) {
|
||||||
segments.removeIf(Segment::tick);
|
|
||||||
}
|
|
||||||
if (segments.isEmpty()) {
|
|
||||||
markDead();
|
markDead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class Segment {
|
|
||||||
Vec3d position;
|
|
||||||
Vector3f offset;
|
|
||||||
|
|
||||||
int age;
|
|
||||||
int maxAge;
|
|
||||||
|
|
||||||
Segment(Vec3d position) {
|
|
||||||
this.position = position;
|
|
||||||
this.offset = new Vector3f((float)(position.getX() - x), (float)(position.getY() - y), (float)(position.getZ() - z));
|
|
||||||
this.maxAge = 90;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getAlpha() {
|
|
||||||
return alpha * (1 - ((float)age / maxAge));
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean tick() {
|
|
||||||
return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge;
|
|
||||||
}
|
|
||||||
|
|
||||||
BezierSegment getPlane(Segment to) {
|
|
||||||
return new BezierSegment(offset, to.offset, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,20 @@
|
||||||
package com.minelittlepony.unicopia.client.particle;
|
package com.minelittlepony.unicopia.client.particle;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
import com.minelittlepony.common.util.Color;
|
|
||||||
import com.minelittlepony.unicopia.EntityConvertable;
|
|
||||||
import com.minelittlepony.unicopia.Unicopia;
|
import com.minelittlepony.unicopia.Unicopia;
|
||||||
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
|
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
|
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Link;
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
import net.minecraft.client.render.BufferBuilder;
|
import net.minecraft.client.render.BufferBuilder;
|
||||||
import net.minecraft.client.render.Tessellator;
|
import net.minecraft.client.render.Tessellator;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.*;
|
import net.minecraft.util.math.*;
|
||||||
|
|
||||||
public class RunesParticle extends OrientedBillboardParticle implements Attachment {
|
@Deprecated
|
||||||
|
public class RunesParticle extends OrientedBillboardParticle {
|
||||||
|
|
||||||
private static final Identifier[] TEXTURES = new Identifier[] {
|
private static final Identifier[] TEXTURES = new Identifier[] {
|
||||||
Unicopia.id("textures/particles/runes_0.png"),
|
Unicopia.id("textures/particles/runes_0.png"),
|
||||||
|
@ -39,10 +33,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
|
||||||
private float prevRotationAngle;
|
private float prevRotationAngle;
|
||||||
private float rotationAngle;
|
private float rotationAngle;
|
||||||
|
|
||||||
private Optional<Link> link = Optional.empty();
|
|
||||||
|
|
||||||
private int stasisAge = -1;
|
|
||||||
|
|
||||||
public RunesParticle(OrientedBillboardParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
|
public RunesParticle(OrientedBillboardParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
|
||||||
super(effect, world, x, y, z, velocityX, velocityY, velocityZ);
|
super(effect, world, x, y, z, velocityX, velocityY, velocityZ);
|
||||||
setMaxAge(70);
|
setMaxAge(70);
|
||||||
|
@ -52,52 +42,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
|
||||||
blue = world.random.nextFloat();
|
blue = world.random.nextFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isStillAlive() {
|
|
||||||
return age < (maxAge - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attach(Link link) {
|
|
||||||
this.link = Optional.of(link);
|
|
||||||
velocityX = 0;
|
|
||||||
velocityY = 0;
|
|
||||||
velocityZ = 0;
|
|
||||||
Vec3d pos = link.get().map(EntityConvertable::asEntity).map(Entity::getPos).orElse(Vec3d.ZERO);
|
|
||||||
setPos(pos.x, pos.y + 0.25, pos.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void detach() {
|
|
||||||
link = Optional.empty();
|
|
||||||
if (targetSize > 1) {
|
|
||||||
this.targetSize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAttribute(int key, Number value) {
|
|
||||||
if (key == ATTR_COLOR) {
|
|
||||||
int tint = value.intValue();
|
|
||||||
red = Color.r(tint);
|
|
||||||
green = Color.g(tint);
|
|
||||||
blue = Color.b(tint);
|
|
||||||
}
|
|
||||||
if (key == ATTR_OPACITY) {
|
|
||||||
alpha = value.floatValue();
|
|
||||||
}
|
|
||||||
if (key == ATTR_RADIUS) {
|
|
||||||
targetSize = value.floatValue();
|
|
||||||
}
|
|
||||||
if (key == ATTR_PITCH) {
|
|
||||||
rotation = new Quaternionf(0, 0, 0, 1);
|
|
||||||
rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(value.floatValue()));
|
|
||||||
}
|
|
||||||
if (key == ATTR_YAW) {
|
|
||||||
rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - value.floatValue()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getScale(float tickDelta) {
|
public float getScale(float tickDelta) {
|
||||||
return MathHelper.lerp(tickDelta, prevBaseSize, baseSize) * super.getScale(tickDelta);
|
return MathHelper.lerp(tickDelta, prevBaseSize, baseSize) * super.getScale(tickDelta);
|
||||||
|
@ -165,15 +109,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
|
||||||
public void tick() {
|
public void tick() {
|
||||||
super.tick();
|
super.tick();
|
||||||
|
|
||||||
link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> {
|
|
||||||
if (getAlphaScale() >= 0.9F) {
|
|
||||||
if (stasisAge < 0) {
|
|
||||||
stasisAge = age;
|
|
||||||
}
|
|
||||||
age = stasisAge;
|
|
||||||
}
|
|
||||||
}, this::detach);
|
|
||||||
|
|
||||||
prevBaseSize = baseSize;
|
prevBaseSize = baseSize;
|
||||||
if (baseSize < targetSize) {
|
if (baseSize < targetSize) {
|
||||||
baseSize += 0.1F;
|
baseSize += 0.1F;
|
||||||
|
|
|
@ -10,22 +10,13 @@ import net.minecraft.client.render.VertexConsumerProvider;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EntityConvertable;
|
|
||||||
import com.minelittlepony.unicopia.client.render.RenderLayers;
|
import com.minelittlepony.unicopia.client.render.RenderLayers;
|
||||||
import com.minelittlepony.unicopia.client.render.model.SphereModel;
|
import com.minelittlepony.unicopia.client.render.model.SphereModel;
|
||||||
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
|
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
|
|
||||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Link;
|
|
||||||
import com.minelittlepony.unicopia.util.ColorHelper;
|
import com.minelittlepony.unicopia.util.ColorHelper;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
import java.util.Optional;
|
public class SphereParticle extends Particle {
|
||||||
|
|
||||||
import com.minelittlepony.common.util.Color;
|
|
||||||
|
|
||||||
public class SphereParticle extends Particle implements Attachment {
|
|
||||||
|
|
||||||
protected float prevRadius;
|
protected float prevRadius;
|
||||||
protected float radius;
|
protected float radius;
|
||||||
|
@ -34,12 +25,6 @@ public class SphereParticle extends Particle implements Attachment {
|
||||||
protected float lerpIncrement;
|
protected float lerpIncrement;
|
||||||
protected float toRadius;
|
protected float toRadius;
|
||||||
|
|
||||||
private Optional<Link> link = Optional.empty();
|
|
||||||
|
|
||||||
private final SphereParticleEffect parameters;
|
|
||||||
|
|
||||||
private boolean bound;
|
|
||||||
|
|
||||||
public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) {
|
public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) {
|
||||||
this(parameters, w, x, y, z);
|
this(parameters, w, x, y, z);
|
||||||
|
|
||||||
|
@ -50,7 +35,6 @@ public class SphereParticle extends Particle implements Attachment {
|
||||||
|
|
||||||
public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z) {
|
public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z) {
|
||||||
super(w, x, y, z);
|
super(w, x, y, z);
|
||||||
this.parameters = parameters;
|
|
||||||
this.radius = parameters.radius();
|
this.radius = parameters.radius();
|
||||||
this.red = parameters.color().x / 255F;
|
this.red = parameters.color().x / 255F;
|
||||||
this.green = parameters.color().y / 255F;
|
this.green = parameters.color().y / 255F;
|
||||||
|
@ -60,43 +44,6 @@ public class SphereParticle extends Particle implements Attachment {
|
||||||
setMaxAge(10);
|
setMaxAge(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isStillAlive() {
|
|
||||||
return age < (maxAge - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attach(Link link) {
|
|
||||||
setMaxAge(50000);
|
|
||||||
this.link = Optional.of(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void detach() {
|
|
||||||
markDead();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAttribute(int key, Number value) {
|
|
||||||
if (key == ATTR_RADIUS) {
|
|
||||||
toRadius = value.floatValue();
|
|
||||||
steps = 20;
|
|
||||||
lerpIncrement = (toRadius - radius) / steps;
|
|
||||||
}
|
|
||||||
if (key == ATTR_COLOR) {
|
|
||||||
int tint = value.intValue();
|
|
||||||
red = Color.r(tint);
|
|
||||||
green = Color.g(tint);
|
|
||||||
blue = Color.b(tint);
|
|
||||||
}
|
|
||||||
if (key == ATTR_OPACITY) {
|
|
||||||
alpha = value.floatValue();
|
|
||||||
}
|
|
||||||
if (key == ATTR_BOUND) {
|
|
||||||
bound = value.intValue() == 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParticleTextureSheet getType() {
|
public ParticleTextureSheet getType() {
|
||||||
return ParticleTextureSheet.CUSTOM;
|
return ParticleTextureSheet.CUSTOM;
|
||||||
|
@ -106,24 +53,7 @@ public class SphereParticle extends Particle implements Attachment {
|
||||||
public void tick() {
|
public void tick() {
|
||||||
super.tick();
|
super.tick();
|
||||||
|
|
||||||
if (link.isPresent()) {
|
radius *= 0.9998281;
|
||||||
link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> {
|
|
||||||
if (!bound) {
|
|
||||||
Vec3d offset = parameters.offset();
|
|
||||||
setPos(e.getX() + offset.getX(), e.getY() + offset.getY(), e.getZ() + offset.getZ());
|
|
||||||
|
|
||||||
prevPosX = e.lastRenderX + offset.getX();
|
|
||||||
prevPosY = e.lastRenderY + offset.getY();
|
|
||||||
prevPosZ = e.lastRenderZ + offset.getZ();
|
|
||||||
}
|
|
||||||
}, this::detach);
|
|
||||||
|
|
||||||
if (steps-- > 0) {
|
|
||||||
radius += lerpIncrement;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
radius *= 0.9998281;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer;
|
import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer;
|
||||||
|
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
|
||||||
import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher;
|
import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher;
|
||||||
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
@ -14,17 +15,19 @@ import net.minecraft.client.render.VertexConsumerProvider;
|
||||||
import net.minecraft.client.render.entity.feature.FeatureRenderer;
|
import net.minecraft.client.render.entity.feature.FeatureRenderer;
|
||||||
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
|
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
|
||||||
import net.minecraft.client.render.entity.model.BipedEntityModel;
|
import net.minecraft.client.render.entity.model.BipedEntityModel;
|
||||||
|
import net.minecraft.client.render.entity.model.EntityModel;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.util.Arm;
|
import net.minecraft.util.Arm;
|
||||||
|
|
||||||
public class AccessoryFeatureRenderer<
|
public class AccessoryFeatureRenderer<
|
||||||
T extends LivingEntity,
|
T extends LivingEntity,
|
||||||
M extends BipedEntityModel<T>> extends FeatureRenderer<T, M> {
|
M extends EntityModel<T>> extends FeatureRenderer<T, M> {
|
||||||
|
|
||||||
private static final List<FeatureFactory<?>> REGISTRY = new ArrayList<>();
|
private static final List<FeatureFactory<?, ?>> REGISTRY = new ArrayList<>();
|
||||||
|
|
||||||
public static void register(FeatureFactory<?>...factories) {
|
@SafeVarargs
|
||||||
|
public static <T extends LivingEntity> void register(FeatureFactory<T, BipedEntityModel<T>>...factories) {
|
||||||
for (var factory : factories) {
|
for (var factory : factories) {
|
||||||
REGISTRY.add(factory);
|
REGISTRY.add(factory);
|
||||||
}
|
}
|
||||||
|
@ -35,11 +38,15 @@ public class AccessoryFeatureRenderer<
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public AccessoryFeatureRenderer(FeatureRendererContext<T, M> context) {
|
public AccessoryFeatureRenderer(FeatureRendererContext<T, M> context) {
|
||||||
super(context);
|
super(context);
|
||||||
features = REGISTRY.stream().map(f -> ((FeatureFactory<T>)f).create(context)).toList();
|
features = REGISTRY.stream().map(f -> ((FeatureFactory<T, M>)f).create(context)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
|
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
|
||||||
|
if (MineLPDelegate.getInstance().getRace(entity).isEquine()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
features.forEach(feature -> feature.render(matrices, vertexConsumers, light, entity, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch));
|
features.forEach(feature -> feature.render(matrices, vertexConsumers, light, entity, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch));
|
||||||
|
|
||||||
Caster.of(entity).ifPresent(caster -> {
|
Caster.of(entity).ifPresent(caster -> {
|
||||||
|
@ -52,6 +59,10 @@ public class AccessoryFeatureRenderer<
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean beforeRenderArms(ArmRenderer sender, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, int light) {
|
public boolean beforeRenderArms(ArmRenderer sender, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, int light) {
|
||||||
|
Caster<?> caster = Caster.of(entity).orElse(null);
|
||||||
|
if (caster != null) {
|
||||||
|
SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, caster, 0, 0, tickDelta, entity.age + tickDelta, 0, 0);
|
||||||
|
}
|
||||||
boolean cancelled = false;
|
boolean cancelled = false;
|
||||||
for (var feature : features) {
|
for (var feature : features) {
|
||||||
cancelled |= feature.beforeRenderArms(sender, tickDelta, matrices, vertexConsumers, entity, light);
|
cancelled |= feature.beforeRenderArms(sender, tickDelta, matrices, vertexConsumers, entity, light);
|
||||||
|
@ -59,8 +70,8 @@ public class AccessoryFeatureRenderer<
|
||||||
return cancelled;
|
return cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface FeatureFactory<T extends LivingEntity> {
|
public interface FeatureFactory<T extends LivingEntity, M extends EntityModel<T>> {
|
||||||
Feature<T> create(FeatureRendererContext<T, ? extends BipedEntityModel<T>> context);
|
Feature<T> create(FeatureRendererContext<T, M> context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Feature<T extends LivingEntity> {
|
public interface Feature<T extends LivingEntity> {
|
||||||
|
@ -75,11 +86,11 @@ public class AccessoryFeatureRenderer<
|
||||||
|
|
||||||
public interface FeatureRoot<
|
public interface FeatureRoot<
|
||||||
T extends LivingEntity,
|
T extends LivingEntity,
|
||||||
M extends BipedEntityModel<T>> {
|
M extends EntityModel<T>> {
|
||||||
AccessoryFeatureRenderer<T, M> getAccessories();
|
AccessoryFeatureRenderer<T, M> getAccessories();
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Nullable
|
@Nullable
|
||||||
static <T extends LivingEntity, M extends BipedEntityModel<T>> FeatureRoot<T, M> of(T entity) {
|
static <T extends LivingEntity, M extends EntityModel<T>> FeatureRoot<T, M> of(T entity) {
|
||||||
var renderer = MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity);
|
var renderer = MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity);
|
||||||
if (renderer instanceof FeatureRoot) {
|
if (renderer instanceof FeatureRoot) {
|
||||||
return (FeatureRoot<T, M>)renderer;
|
return (FeatureRoot<T, M>)renderer;
|
||||||
|
|
|
@ -39,6 +39,15 @@ public final class RenderLayers extends RenderLayer {
|
||||||
.target(TRANSLUCENT_TARGET)
|
.target(TRANSLUCENT_TARGET)
|
||||||
.build(false));
|
.build(false));
|
||||||
|
|
||||||
|
private static final RenderLayer MAGIC_SHIELD = of("magic_shield", VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL,
|
||||||
|
VertexFormat.DrawMode.QUADS, 256, true, true, MultiPhaseParameters.builder()
|
||||||
|
.program(COLOR_PROGRAM)
|
||||||
|
.transparency(TRANSLUCENT_TRANSPARENCY)
|
||||||
|
.target(TRANSLUCENT_TARGET)
|
||||||
|
.cull(DISABLE_CULLING)
|
||||||
|
.writeMaskState(COLOR_MASK)
|
||||||
|
.build(false));
|
||||||
|
|
||||||
private static final Function<Integer, RenderLayer> MAGIC_COLORIN_FUNC = Util.memoize(color -> {
|
private static final Function<Integer, RenderLayer> MAGIC_COLORIN_FUNC = Util.memoize(color -> {
|
||||||
return of("magic_colored_" + color,
|
return of("magic_colored_" + color,
|
||||||
VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL,
|
VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL,
|
||||||
|
@ -75,6 +84,10 @@ public final class RenderLayers extends RenderLayer {
|
||||||
return MAGIC_NO_COLOR;
|
return MAGIC_NO_COLOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RenderLayer getMagicShield() {
|
||||||
|
return MAGIC_SHIELD;
|
||||||
|
}
|
||||||
|
|
||||||
public static RenderLayer getMagicColored() {
|
public static RenderLayer getMagicColored() {
|
||||||
return MAGIC_COLORED;
|
return MAGIC_COLORED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,31 +11,46 @@ import net.minecraft.client.render.VertexFormats;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
|
|
||||||
public class RenderUtil {
|
public class RenderUtil {
|
||||||
private static final Vector4f TEMP_VECTOR = new Vector4f();
|
public static final Vector4f TEMP_VECTOR = new Vector4f();
|
||||||
|
private static final Vector4f TEMP_UV_VECTOR = new Vector4f();
|
||||||
public static final Vertex[] UNIT_FACE = new Vertex[] {
|
public static final Vertex[] UNIT_FACE = new Vertex[] {
|
||||||
new Vertex(new Vector3f(0, 0, 0), 1, 1),
|
new Vertex(0, 0, 0, 1, 1),
|
||||||
new Vertex(new Vector3f(0, 1, 0), 1, 0),
|
new Vertex(0, 1, 0, 1, 0),
|
||||||
new Vertex(new Vector3f(1, 1, 0), 0, 0),
|
new Vertex(1, 1, 0, 0, 0),
|
||||||
new Vertex(new Vector3f(1, 0, 0), 0, 1)
|
new Vertex(1, 0, 0, 0, 1)
|
||||||
|
};
|
||||||
|
public static final Vertex[] FRAME_BUFFER_VERTICES = new Vertex[] {
|
||||||
|
new Vertex(0, 1, 0, 0, 0),
|
||||||
|
new Vertex(1, 1, 0, 1, 0),
|
||||||
|
new Vertex(1, 0, 0, 1, 1),
|
||||||
|
new Vertex(0, 0, 0, 0, 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) {
|
public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) {
|
||||||
|
renderFace(matrices, te, buffer, r, g, b, a, light, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light, float uScale, float vScale) {
|
||||||
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
|
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
|
||||||
|
Matrix4f positionmatrix = matrices.peek().getPositionMatrix();
|
||||||
Vertex[] UNIT_FACE = new Vertex[] {
|
|
||||||
new Vertex(new Vector3f(0, 0, 0), 1, 1),
|
|
||||||
new Vertex(new Vector3f(0, 1, 0), 1, 0),
|
|
||||||
new Vertex(new Vector3f(1, 1, 0), 0, 0),
|
|
||||||
new Vertex(new Vector3f(1, 0, 0), 0, 1)
|
|
||||||
};
|
|
||||||
|
|
||||||
Matrix4f transformation = matrices.peek().getPositionMatrix();
|
|
||||||
for (Vertex vertex : UNIT_FACE) {
|
for (Vertex vertex : UNIT_FACE) {
|
||||||
transformation.transform(TEMP_VECTOR.set(vertex.position(), 1));
|
Vector4f position = vertex.position(positionmatrix);
|
||||||
buffer.vertex(TEMP_VECTOR.x, TEMP_VECTOR.y, TEMP_VECTOR.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next();
|
buffer.vertex(position.x, position.y, position.z).texture(vertex.texture().x * uScale, vertex.texture().y * vScale).color(r, g, b, a).light(light).next();
|
||||||
}
|
}
|
||||||
te.draw();
|
te.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
record Vertex(Vector3f position, float u, float v) {}
|
public record Vertex(Vector3f position, Vector3f texture) {
|
||||||
|
public Vertex(float x, float y, float z, float u, float v) {
|
||||||
|
this(new Vector3f(x, y, z), new Vector3f(u, v, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector4f position(Matrix4f mat) {
|
||||||
|
return mat.transform(TEMP_VECTOR.set(position, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector4f texture(Matrix4f mat) {
|
||||||
|
return mat.transform(TEMP_UV_VECTOR.set(texture, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,22 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.client.render.RenderUtil;
|
||||||
|
|
||||||
public record BezierSegment(
|
public record BezierSegment(
|
||||||
Vector3f[] corners
|
RenderUtil.Vertex[] corners
|
||||||
) {
|
) {
|
||||||
|
|
||||||
public BezierSegment(Vector3f from, Vector3f to, float height) {
|
public BezierSegment(Vector3f from, Vector3f to, float height) {
|
||||||
this(new Vector3f[] {
|
this(new RenderUtil.Vertex[] {
|
||||||
new Vector3f(from.x, from.y - height/2F, from.z), // bottom left
|
new RenderUtil.Vertex(from.x, from.y - height/2F, from.z, 0, 0), // bottom left
|
||||||
new Vector3f(from.x, from.y + height/2F, from.z), // top left
|
new RenderUtil.Vertex(from.x, from.y + height/2F, from.z, 1, 0), // top left
|
||||||
new Vector3f(to.x, to.y + height/2F, to.z), // top right
|
new RenderUtil.Vertex(to.x, to.y + height/2F, to.z, 1, 1), // top right
|
||||||
new Vector3f(to.x, to.y - height/2F, to.z) // bottom right
|
new RenderUtil.Vertex(to.x, to.y - height/2F, to.z, 0, 1) // bottom right
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forEachCorner(Consumer<Vector3f> transformer) {
|
public void forEachCorner(Consumer<RenderUtil.Vertex> transformer) {
|
||||||
for (var corner : corners) {
|
for (var corner : corners) {
|
||||||
transformer.accept(corner);
|
transformer.accept(corner);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
package com.minelittlepony.unicopia.client.render.bezier;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
|
|
||||||
public class Ribbon implements Iterable<BezierSegment> {
|
|
||||||
public float width;
|
|
||||||
public float angle;
|
|
||||||
|
|
||||||
private final List<Node> nodes = new ArrayList<>();
|
|
||||||
private final List<BezierSegment> segments = new ArrayList<>();
|
|
||||||
private Node lastNode;
|
|
||||||
|
|
||||||
public Ribbon(Vec3d position, Vector3f bottom, Vector3f top, float angle) {
|
|
||||||
this.angle = angle;
|
|
||||||
lastNode = new Node(position, position.toVector3f().add(bottom), position.toVector3f().add(top));
|
|
||||||
nodes.add(lastNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addNode(Vec3d position, float angle) {
|
|
||||||
Vector3f directionVector = position.subtract(lastNode.position()).toVector3f();
|
|
||||||
|
|
||||||
Vector3f bottom = lastNode.bottom().add(directionVector).rotateAxis(angle, directionVector.x, directionVector.y, directionVector.z);
|
|
||||||
Vector3f top = lastNode.top().add(directionVector).rotateAxis(angle, directionVector.x, directionVector.y, directionVector.z);
|
|
||||||
|
|
||||||
Node oldNode = lastNode;
|
|
||||||
lastNode = new Node(position, bottom, top);
|
|
||||||
nodes.add(lastNode);
|
|
||||||
segments.add(new BezierSegment(new Vector3f[] {
|
|
||||||
oldNode.bottom(),
|
|
||||||
oldNode.top(),
|
|
||||||
lastNode.bottom(),
|
|
||||||
lastNode.top()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<BezierSegment> iterator() {
|
|
||||||
return segments.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
record Node(Vec3d position, Vector3f bottom, Vector3f top) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.minelittlepony.unicopia.client.render.bezier;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
|
public class Trail {
|
||||||
|
|
||||||
|
private final List<Segment> segments = new ArrayList<>();
|
||||||
|
|
||||||
|
public final Vec3d pos;
|
||||||
|
|
||||||
|
public Trail(Vec3d pos) {
|
||||||
|
this.pos = pos;
|
||||||
|
segments.add(new Segment(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Segment> getSegments() {
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(Vec3d newPosition) {
|
||||||
|
if (segments.isEmpty()) {
|
||||||
|
segments.add(new Segment(newPosition));
|
||||||
|
} else {
|
||||||
|
Vec3d last = segments.get(segments.size() - 1).position;
|
||||||
|
if (newPosition.distanceTo(last) > 0.2) {
|
||||||
|
segments.add(new Segment(newPosition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean tick() {
|
||||||
|
if (segments.size() > 1) {
|
||||||
|
segments.removeIf(Segment::tick);
|
||||||
|
}
|
||||||
|
return segments.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Segment {
|
||||||
|
Vec3d position;
|
||||||
|
Vector3f offset;
|
||||||
|
|
||||||
|
int age;
|
||||||
|
int maxAge;
|
||||||
|
|
||||||
|
Segment(Vec3d position) {
|
||||||
|
this.position = position;
|
||||||
|
this.offset = position.subtract(pos).toVector3f();
|
||||||
|
this.maxAge = 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getAlpha() {
|
||||||
|
return (1 - ((float)age / maxAge));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean tick() {
|
||||||
|
return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BezierSegment getPlane(Segment to) {
|
||||||
|
return new BezierSegment(offset, to.offset, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue