mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-24 13:57:59 +01:00
Merge branch '1.20.2' into 1.20.4
# Conflicts: # src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java # src/main/java/com/minelittlepony/unicopia/block/UBlocks.java # src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java # src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java # src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java # src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java # src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java
This commit is contained in:
commit
398ed0743d
487 changed files with 9510 additions and 2897 deletions
|
@ -3,7 +3,7 @@
|
||||||
[![ru](https://img.shields.io/badge/lang-ru-d52b1e.svg)](README_RU.md)
|
[![ru](https://img.shields.io/badge/lang-ru-d52b1e.svg)](README_RU.md)
|
||||||
[![cn](https://img.shields.io/badge/lang-cn-de2910.svg)](README_CN.md)
|
[![cn](https://img.shields.io/badge/lang-cn-de2910.svg)](README_CN.md)
|
||||||
|
|
||||||
When starting a new world you're given the choice of which tribe to join. One of Unicorn, Pegasus, Earth, Bat, or Changeling.
|
When starting a new world you're given the choice of which tribe to join. You can choose from _Unicorn_, _Pegasus_, _Earth Pony_, _Batpony_, or _Changeling_.
|
||||||
|
|
||||||
Depending on which race you pick, you're given different abilities, displayed on your HUD in a series of circular elements.
|
Depending on which race you pick, you're given different abilities, displayed on your HUD in a series of circular elements.
|
||||||
To activate an ability, simply press and hold the key corresponding to that ability. Some take longer than others, and certain abilities
|
To activate an ability, simply press and hold the key corresponding to that ability. Some take longer than others, and certain abilities
|
||||||
|
@ -17,7 +17,7 @@ also respond to quick, short single-taps, or double-taps.
|
||||||
|
|
||||||
For unicorns, casting spells is done through gems, which you can obtain whilst mining. You first need to craft a spellbook using a gem, and then
|
For unicorns, casting spells is done through gems, which you can obtain whilst mining. You first need to craft a spellbook using a gem, and then
|
||||||
in the spellbook you can discover the magical traits of different items and recipes to combine them to create different spells, as well
|
in the spellbook you can discover the magical traits of different items and recipes to combine them to create different spells, as well
|
||||||
as modify existing one.
|
as modify existing ones.
|
||||||
|
|
||||||
Once you have a gem with a spell you want to use, you can equip it to your main-hand or off-hand slot by right-clicking with a gem in
|
Once you have a gem with a spell you want to use, you can equip it to your main-hand or off-hand slot by right-clicking with a gem in
|
||||||
one of either hand, then to activate it you us your primary ability. You can also cast spells directly from a gem by using the ability
|
one of either hand, then to activate it you us your primary ability. You can also cast spells directly from a gem by using the ability
|
||||||
|
@ -34,7 +34,7 @@ also respond to quick, short single-taps, or double-taps.
|
||||||
### Earth Ponies
|
### Earth Ponies
|
||||||
- Kicking & Stomping
|
- Kicking & Stomping
|
||||||
|
|
||||||
Earth ponies kick good.
|
*Earth ponies kick good*.
|
||||||
|
|
||||||
If Mine Little Pony is installed, and you have the appearance of a pony, kicking will target the block behind you, so twirl that rump!
|
If Mine Little Pony is installed, and you have the appearance of a pony, kicking will target the block behind you, so twirl that rump!
|
||||||
Kicking blocks will incrementally mine them, and kicking trees will shake items loose from their branches. Kicking a tree _too much_ might destroy it,
|
Kicking blocks will incrementally mine them, and kicking trees will shake items loose from their branches. Kicking a tree _too much_ might destroy it,
|
||||||
|
@ -47,7 +47,7 @@ also respond to quick, short single-taps, or double-taps.
|
||||||
|
|
||||||
- Bracing
|
- Bracing
|
||||||
|
|
||||||
Earth ponies can brace themselves by sneaking! It, uh, makes you harder to push! yeah!
|
Earth ponies can brace themselves by sneaking! It, uh, makes you harder to push! Yeah!
|
||||||
|
|
||||||
### Pegasi / Bat Ponies
|
### Pegasi / Bat Ponies
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ also respond to quick, short single-taps, or double-taps.
|
||||||
|
|
||||||
- Hanging of Ceilings
|
- Hanging of Ceilings
|
||||||
|
|
||||||
Ever just want to hang out? Well bat ponies can, _literally_! All the cool kids are doing.
|
Ever just want to hang out? Well bat ponies can, _literally_! All the cool kids are doing it!
|
||||||
|
|
||||||
- Mangoes
|
- Mangoes
|
||||||
|
|
||||||
|
@ -122,6 +122,12 @@ also respond to quick, short single-taps, or double-taps.
|
||||||
Changelings can turn into damn near anything, even other players! And blocks! And hostile mobs!
|
Changelings can turn into damn near anything, even other players! And blocks! And hostile mobs!
|
||||||
Careful about turning into skeletons, though, because they hate the sun even more than bat ponies.
|
Careful about turning into skeletons, though, because they hate the sun even more than bat ponies.
|
||||||
|
|
||||||
|
- Crawling
|
||||||
|
|
||||||
|
Changelings can crawl on Ceilings, which is very useful for lining up your shot, when attempting to shoot somepony in a cave,
|
||||||
|
while disguised as a stone block. Or is that just me? Anyways, they can only crawl along flat surfaces, meaning that if you go off the edge of a block,
|
||||||
|
you will fall off.
|
||||||
|
|
||||||
## Special Items, Plants, Tools
|
## Special Items, Plants, Tools
|
||||||
|
|
||||||
### Polearms
|
### Polearms
|
||||||
|
@ -135,7 +141,7 @@ Fighting too close together with them may cause you some knockback.
|
||||||
|
|
||||||
Zap Apple Trees occur naturally in the world and are the only way to obtain zap apples in survival. They can appear in one of several different states:
|
Zap Apple Trees occur naturally in the world and are the only way to obtain zap apples in survival. They can appear in one of several different states:
|
||||||
|
|
||||||
Hybernating, Flowering, Fruiting, or Withering
|
*Hibernating, Flowering, Fruiting, or Withering*
|
||||||
|
|
||||||
They cycle through these states throughout the lunar cycle, so if you find one, and it's not in the state you want, wait around another few days and it
|
They cycle through these states throughout the lunar cycle, so if you find one, and it's not in the state you want, wait around another few days and it
|
||||||
will eventually bear fruit, but don't try to harvest the apples before they're ripe, because they will zap you!
|
will eventually bear fruit, but don't try to harvest the apples before they're ripe, because they will zap you!
|
||||||
|
@ -145,3 +151,5 @@ If you're able to obtain the wood and leaves, it also makes the perfect deterant
|
||||||
### Muffins
|
### Muffins
|
||||||
|
|
||||||
They're bouncy and delicious, and pigs absolutely love them!
|
They're bouncy and delicious, and pigs absolutely love them!
|
||||||
|
|
||||||
|
<!-- https://media.tenor.com/GTYRNMAuLIUAAAAC/muffin-button-muffins.gif -->
|
10
README.md
10
README.md
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
Bringing the magic of friendship to Minecraft!
|
Bringing the magic of friendship to Minecraft!
|
||||||
|
|
||||||
What started as a humble utility to make playing as a unicorn a little more emersive has grown into a full-blown pony
|
What started as a humble utility to make playing as a unicorn a little more immersive has grown into a full-blown pony
|
||||||
conversion experience that brings new magic, mechanics and experience to the world of Minecraft to make it truly feel like you've
|
conversion experience that brings new magic, mechanics and experience to the world of Minecraft to make it truly feel like you've
|
||||||
entered the world of Equestria!
|
entered the world of Equestria!
|
||||||
|
|
||||||
|
@ -24,13 +24,13 @@ Unicorns, Pegasi, Earth Ponies, and even Changelings get their own special abili
|
||||||
- *Play as a unicorn* and learn to use magic! Craft your first spellbook and experiment, finding the different spells you can
|
- *Play as a unicorn* and learn to use magic! Craft your first spellbook and experiment, finding the different spells you can
|
||||||
make and what they do, or simply delve into the lore to learn more about the past of this mysterious world!
|
make and what they do, or simply delve into the lore to learn more about the past of this mysterious world!
|
||||||
|
|
||||||
Besides casting spells, such as a shield to protect themselves, of fire a bolt of magic to incinerate your foes,
|
Besides casting spells, such as a shield to protect themselves, or a bolt of magic to incinerate your foes,
|
||||||
Unicorns can also teleport to get around obstacles or simply reach those hard to reach places.
|
Unicorns can also teleport to get around obstacles or simply reach those hard to reach places.
|
||||||
|
|
||||||
- *Play as a pegasus* and dominate the skies! Besides the ability to fly, pegasi can also perform rainbooms, control the weather by shoving them into jars,
|
- *Play as a pegasus* and dominate the skies! Besides the ability to fly, pegasi can also perform sonic rainbooms, control the weather by shoving them into jars,
|
||||||
and have a greater reach distance and speed than other races.
|
and have a greater reach distance and speed than other races.
|
||||||
|
|
||||||
- *Play as a humble background pony*! Earth Ponies are tougher and heavier than the other races. They also have the nify ability to
|
- *Play as a humble background pony*! Earth Ponies are tougher and heavier than the other races. They also have the nifty ability to
|
||||||
kick trees to get food and hasten the growth of crops. You'll never go hungry if you're an earth pony.
|
kick trees to get food and hasten the growth of crops. You'll never go hungry if you're an earth pony.
|
||||||
|
|
||||||
Feeling like going over to the dark side?
|
Feeling like going over to the dark side?
|
||||||
|
@ -58,7 +58,7 @@ Unicorns, Pegasi, Earth Ponies, and even Changelings get their own special abili
|
||||||
|
|
||||||
- Airflow is simulated (badly)
|
- Airflow is simulated (badly)
|
||||||
Pegasi, beware about flying during storms! It can get dangerous out there!
|
Pegasi, beware about flying during storms! It can get dangerous out there!
|
||||||
If you're playing as a flying species, or just like having nice things, try building a weather vein.
|
If you're playing as a flying species, or just like having nice things, try building a weather vane.
|
||||||
It shows the actual, totally real and not simulated badly, wind direction of your minecraft world. Just beware
|
It shows the actual, totally real and not simulated badly, wind direction of your minecraft world. Just beware
|
||||||
that the direction and strength is situational (and bad), and will be different depending where you are and
|
that the direction and strength is situational (and bad), and will be different depending where you are and
|
||||||
how high up you are.
|
how high up you are.
|
||||||
|
|
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.value().checkPattern(player.asWorld(), pos))
|
||||||
|
.filter(result -> result.matchedLocations().size() + 1 >= TransformCropsRecipe.MINIMUM_INPUT)
|
||||||
|
.filter(result -> {
|
||||||
|
boolean transform = result.shoudTransform(player.asWorld().random);
|
||||||
|
|
||||||
|
player.playSound(USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, 1);
|
||||||
|
|
||||||
|
result.matchedLocations().forEach(cell -> {
|
||||||
|
spawnConversionParticles(player.asWorld(), cell.up(), false);
|
||||||
|
BlockDestructionManager manager = BlockDestructionManager.of(player.asWorld());
|
||||||
|
if (transform) {
|
||||||
|
if (manager.damageBlock(cell, 8) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) {
|
||||||
|
player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState());
|
||||||
|
player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (manager.damageBlock(cell, 4) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) {
|
||||||
|
player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState());
|
||||||
|
player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
spawnConversionParticles(player.asWorld(), pos, transform);
|
||||||
|
if (transform) {
|
||||||
|
player.asWorld().setBlockState(pos, result.recipe().getResult(player.asWorld(), pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.findFirst()
|
||||||
|
.isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void spawnConversionParticles(World w, BlockPos pos, boolean success) {
|
||||||
|
DoubleSupplier vecComponentFactory = () -> w.random.nextTriangular(0, 0.5);
|
||||||
|
Supplier<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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,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 {
|
||||||
public static final MapCodec<BaseZapAppleLeavesBlock> CODEC = LeavesBlock.createCodec(BaseZapAppleLeavesBlock::new);
|
public static final MapCodec<BaseZapAppleLeavesBlock> CODEC = LeavesBlock.createCodec(BaseZapAppleLeavesBlock::new);
|
||||||
|
|
||||||
public static Settings settings() {
|
public static Settings settings() {
|
||||||
|
@ -39,61 +39,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,40 +92,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,66 @@
|
||||||
|
package com.minelittlepony.unicopia.block;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.ability.EarthPonyGrowAbility.Growable;
|
||||||
|
import com.minelittlepony.unicopia.entity.mob.IgnominiousBulbEntity;
|
||||||
|
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.FlowerBlock;
|
||||||
|
import net.minecraft.block.SuspiciousStewIngredient;
|
||||||
|
import net.minecraft.client.util.ParticleUtil;
|
||||||
|
import net.minecraft.entity.Dismounting;
|
||||||
|
import net.minecraft.entity.effect.StatusEffect;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
public class CuringJokeBlock extends FlowerBlock implements Growable {
|
||||||
|
private static final MapCodec<CuringJokeBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
STEW_EFFECT_CODEC.forGetter(FlowerBlock::getStewEffects),
|
||||||
|
FlowerBlock.createSettingsCodec()
|
||||||
|
).apply(instance, CuringJokeBlock::new));
|
||||||
|
|
||||||
|
public CuringJokeBlock(StatusEffect suspiciousStewEffect, int effectDuration, Settings settings) {
|
||||||
|
super(suspiciousStewEffect, effectDuration, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CuringJokeBlock(List<SuspiciousStewIngredient.StewEffect> effects, Settings settings) {
|
||||||
|
super(effects, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends FlowerBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void randomDisplayTick(BlockState state, World world, BlockPos pos, Random random) {
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
ParticleUtil.spawnParticle(world, pos, random, new MagicParticleEffect(0x3388EE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean grow(World world, BlockState state, BlockPos pos) {
|
||||||
|
var otherFlowers = BlockPos.streamOutwards(pos, 16, 16, 16)
|
||||||
|
.filter(p -> world.getBlockState(p).isOf(this))
|
||||||
|
.map(BlockPos::toImmutable)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
IgnominiousBulbEntity bulb = new IgnominiousBulbEntity(world);
|
||||||
|
bulb.setBaby(true);
|
||||||
|
bulb.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0);
|
||||||
|
|
||||||
|
if (Dismounting.canPlaceEntityAt(world, bulb, bulb.getBoundingBox())) {
|
||||||
|
otherFlowers.forEach(p -> world.breakBlock(p, false));
|
||||||
|
world.spawnEntity(bulb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
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,31 @@
|
||||||
|
package com.minelittlepony.unicopia.block;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.state.StateManager;
|
||||||
|
import net.minecraft.state.property.BooleanProperty;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
|
|
||||||
|
public class EnchantedFruitBlock extends FruitBlock {
|
||||||
|
static final BooleanProperty ENCHANTED = BooleanProperty.of("enchanted");
|
||||||
|
|
||||||
|
private static final MapCodec<EnchantedFruitBlock> CODEC = createCodec(EnchantedFruitBlock::new);
|
||||||
|
|
||||||
|
public EnchantedFruitBlock(Direction attachmentFace, Block stem, VoxelShape shape, boolean flammable, Settings settings) {
|
||||||
|
super(attachmentFace, stem, shape, flammable, settings);
|
||||||
|
setDefaultState(getDefaultState().with(ENCHANTED, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends FruitBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
|
super.appendProperties(builder);
|
||||||
|
builder.add(ENCHANTED);
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ public class FancyBedBlock extends BedBlock {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
private final String base;
|
protected final String base;
|
||||||
|
|
||||||
public FancyBedBlock(String base, Settings settings) {
|
public FancyBedBlock(String base, Settings settings) {
|
||||||
super(DyeColor.WHITE, settings);
|
super(DyeColor.WHITE, settings);
|
||||||
|
|
|
@ -42,10 +42,10 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka
|
||||||
|
|
||||||
public static final List<FruitBearingBlock> REGISTRY = new ArrayList<>();
|
public static final List<FruitBearingBlock> REGISTRY = new ArrayList<>();
|
||||||
|
|
||||||
private final Supplier<Block> fruit;
|
protected final Supplier<Block> fruit;
|
||||||
private final Supplier<ItemStack> rottenFruitSupplier;
|
protected final Supplier<ItemStack> rottenFruitSupplier;
|
||||||
|
|
||||||
private final int overlay;
|
protected final int overlay;
|
||||||
|
|
||||||
public FruitBearingBlock(int overlay, Supplier<Block> fruit, Supplier<ItemStack> rottenFruitSupplier, Settings settings) {
|
public FruitBearingBlock(int overlay, Supplier<Block> fruit, Supplier<ItemStack> rottenFruitSupplier, Settings settings) {
|
||||||
super(settings
|
super(settings
|
||||||
|
@ -78,6 +78,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);
|
||||||
|
@ -86,10 +94,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) {
|
||||||
|
@ -105,7 +117,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.block;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.ability.EarthPonyKickAbility.Buckable;
|
import com.minelittlepony.unicopia.ability.EarthPonyKickAbility.Buckable;
|
||||||
|
import com.mojang.datafixers.util.Function5;
|
||||||
import com.mojang.serialization.Codec;
|
import com.mojang.serialization.Codec;
|
||||||
import com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
@ -23,20 +24,24 @@ public class FruitBlock extends Block implements Buckable {
|
||||||
public static final int DEFAULT_FRUIT_SIZE = 5;
|
public static final int DEFAULT_FRUIT_SIZE = 5;
|
||||||
public static final double DEFAULT_STEM_OFFSET = 2.6F;
|
public static final double DEFAULT_STEM_OFFSET = 2.6F;
|
||||||
public static final VoxelShape DEFAULT_SHAPE = createFruitShape(DEFAULT_STEM_OFFSET, DEFAULT_FRUIT_SIZE);
|
public static final VoxelShape DEFAULT_SHAPE = createFruitShape(DEFAULT_STEM_OFFSET, DEFAULT_FRUIT_SIZE);
|
||||||
public static final MapCodec<FruitBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
private static final MapCodec<FruitBlock> CODEC = createCodec(FruitBlock::new);
|
||||||
Direction.CODEC.fieldOf("attachment_face").forGetter(b -> b.attachmentFace),
|
|
||||||
Registries.BLOCK.getCodec().fieldOf("stem").forGetter(b -> b.stem),
|
|
||||||
RecordCodecBuilder.<VoxelShape>create(i -> i.group(
|
|
||||||
Codec.DOUBLE.fieldOf("stem_offset").forGetter(b -> (double)0),
|
|
||||||
Codec.DOUBLE.fieldOf("fruit_offset").forGetter(b -> (double)0)
|
|
||||||
).apply(i, FruitBlock::createFruitShape)).fieldOf("shape").forGetter(b -> b.shape),
|
|
||||||
Codec.BOOL.fieldOf("flammable").forGetter(b -> false),
|
|
||||||
BedBlock.createSettingsCodec()
|
|
||||||
).apply(instance, FruitBlock::new));
|
|
||||||
|
|
||||||
private final Direction attachmentFace;
|
protected final Direction attachmentFace;
|
||||||
private final Block stem;
|
protected final Block stem;
|
||||||
private final VoxelShape shape;
|
protected final VoxelShape shape;
|
||||||
|
|
||||||
|
public static <T extends FruitBlock> MapCodec<T> createCodec(Function5<Direction, Block, VoxelShape, Boolean, Settings, T> constructor) {
|
||||||
|
return RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
Direction.CODEC.fieldOf("attachment_face").forGetter(b -> b.attachmentFace),
|
||||||
|
Registries.BLOCK.getCodec().fieldOf("stem").forGetter(b -> b.stem),
|
||||||
|
RecordCodecBuilder.<VoxelShape>create(i -> i.group(
|
||||||
|
Codec.DOUBLE.fieldOf("stem_offset").forGetter(b -> (double)0),
|
||||||
|
Codec.DOUBLE.fieldOf("fruit_offset").forGetter(b -> (double)0)
|
||||||
|
).apply(i, FruitBlock::createFruitShape)).fieldOf("shape").forGetter(b -> b.shape),
|
||||||
|
Codec.BOOL.fieldOf("flammable").forGetter(b -> false),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, constructor));
|
||||||
|
}
|
||||||
|
|
||||||
public static VoxelShape createFruitShape(double stemOffset, double fruitSize) {
|
public static VoxelShape createFruitShape(double stemOffset, double fruitSize) {
|
||||||
final double min = (16 - fruitSize) * 0.5;
|
final double min = (16 - fruitSize) * 0.5;
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.minelittlepony.unicopia.block;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.util.CodecUtils;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
|
||||||
|
public class GoldenOakLeavesBlock extends FruitBearingBlock {
|
||||||
|
private static final MapCodec<GoldenOakLeavesBlock> CODEC = RecordCodecBuilder.<GoldenOakLeavesBlock>mapCodec(instance -> instance.group(
|
||||||
|
Codec.INT.fieldOf("overlay").forGetter(b -> b.overlay),
|
||||||
|
CodecUtils.supplierOf(Registries.BLOCK.getCodec()).fieldOf("fruit").forGetter(b -> b.fruit),
|
||||||
|
CodecUtils.supplierOf(ItemStack.CODEC).fieldOf("rotten_fruit").forGetter(b -> b.rottenFruitSupplier),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, GoldenOakLeavesBlock::new));
|
||||||
|
|
||||||
|
public GoldenOakLeavesBlock(int overlay, Supplier<Block> fruit, Supplier<ItemStack> rottenFruitSupplier, Settings settings) {
|
||||||
|
super(overlay, fruit, rottenFruitSupplier, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends GoldenOakLeavesBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldAdvance(Random random) {
|
||||||
|
return random.nextInt(1000) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BlockState getPlacedFruitState(Random random) {
|
||||||
|
return super.getPlacedFruitState(random).with(EnchantedFruitBlock.ENCHANTED, random.nextInt(1000) == 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
@ -136,6 +138,15 @@ public interface UBlocks {
|
||||||
() -> UItems.APPLE_PIE_HOOF,
|
() -> UItems.APPLE_PIE_HOOF,
|
||||||
Settings.create().solid().mapColor(MapColor.ORANGE).strength(0.5F).sounds(BlockSoundGroup.WOOL).pistonBehavior(PistonBehavior.DESTROY)
|
Settings.create().solid().mapColor(MapColor.ORANGE).strength(0.5F).sounds(BlockSoundGroup.WOOL).pistonBehavior(PistonBehavior.DESTROY)
|
||||||
));
|
));
|
||||||
|
Block GOLDEN_OAK_LEAVES = register("golden_oak_leaves", new GoldenOakLeavesBlock(
|
||||||
|
MapColor.GOLD.color,
|
||||||
|
() -> UBlocks.GOLDEN_APPLE,
|
||||||
|
() -> Items.GOLDEN_APPLE.getDefaultStack(),
|
||||||
|
FabricBlockSettings.copy(Blocks.OAK_LEAVES)
|
||||||
|
), ItemGroups.NATURAL);
|
||||||
|
Block GOLDEN_APPLE = register("golden_apple", new EnchantedFruitBlock(Direction.DOWN, GOLDEN_OAK_LEAVES, FruitBlock.DEFAULT_SHAPE, false, Settings.create().mapColor(MapColor.GOLD)));
|
||||||
|
Block GOLDEN_OAK_SPROUT = register("golden_oak_sprout", new SproutBlock(0xE5FFCC88, () -> UItems.GOLDEN_OAK_SEEDS, () -> UTreeGen.GOLDEN_APPLE_TREE.sapling().map(Block::getDefaultState).get(), SproutBlock.settings()));
|
||||||
|
Block GOLDEN_OAK_LOG = register("golden_oak_log", BlockConstructionUtils.createLogBlock(MapColor.OFF_WHITE, MapColor.GOLD), ItemGroups.BUILDING_BLOCKS);
|
||||||
|
|
||||||
SegmentedCropBlock OATS = register("oats", SegmentedCropBlock.create(11, 5, () -> UItems.OAT_SEEDS, null, null, AbstractBlock.Settings.copy(Blocks.WHEAT)));
|
SegmentedCropBlock OATS = register("oats", SegmentedCropBlock.create(11, 5, () -> UItems.OAT_SEEDS, null, null, AbstractBlock.Settings.copy(Blocks.WHEAT)));
|
||||||
SegmentedCropBlock OATS_STEM = register("oats_stem", OATS.createNext(5));
|
SegmentedCropBlock OATS_STEM = register("oats_stem", OATS.createNext(5));
|
||||||
|
@ -143,6 +154,13 @@ public interface UBlocks {
|
||||||
|
|
||||||
Block PLUNDER_VINE = register("plunder_vine", new ThornBlock(() -> UBlocks.PLUNDER_VINE_BUD, Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).ticksRandomly().sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY)));
|
Block PLUNDER_VINE = register("plunder_vine", new ThornBlock(() -> UBlocks.PLUNDER_VINE_BUD, Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).ticksRandomly().sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY)));
|
||||||
Block PLUNDER_VINE_BUD = register("plunder_vine_bud", new ThornBudBlock(PLUNDER_VINE.getDefaultState(), Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).nonOpaque().ticksRandomly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY)));
|
Block PLUNDER_VINE_BUD = register("plunder_vine_bud", new ThornBudBlock(PLUNDER_VINE.getDefaultState(), Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).nonOpaque().ticksRandomly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY)));
|
||||||
|
CuringJokeBlock CURING_JOKE = register("curing_joke", new CuringJokeBlock(UEffects.BUTTER_FINGERS, 7, AbstractBlock.Settings.create().mapColor(MapColor.PALE_PURPLE).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).offset(AbstractBlock.OffsetType.XZ).pistonBehavior(PistonBehavior.DESTROY)));
|
||||||
|
Block GOLD_ROOT = register("gold_root", new CarrotsBlock(AbstractBlock.Settings.create().mapColor(MapColor.GOLD).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP).pistonBehavior(PistonBehavior.DESTROY)) {
|
||||||
|
@Override
|
||||||
|
protected ItemConvertible getSeedsItem() {
|
||||||
|
return Items.GOLDEN_CARROT;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Block CHITIN = register("chitin", new SnowyBlock(Settings.create().mapColor(MapColor.PALE_PURPLE).hardness(5).requiresTool().ticksRandomly().sounds(BlockSoundGroup.CORAL)), ItemGroups.NATURAL);
|
Block CHITIN = register("chitin", new SnowyBlock(Settings.create().mapColor(MapColor.PALE_PURPLE).hardness(5).requiresTool().ticksRandomly().sounds(BlockSoundGroup.CORAL)), ItemGroups.NATURAL);
|
||||||
Block SURFACE_CHITIN = register("surface_chitin", new GrowableBlock(() -> CHITIN, Settings.copy(CHITIN)), ItemGroups.NATURAL);
|
Block SURFACE_CHITIN = register("surface_chitin", new GrowableBlock(() -> CHITIN, Settings.copy(CHITIN)), ItemGroups.NATURAL);
|
||||||
|
@ -156,49 +174,42 @@ public interface UBlocks {
|
||||||
Block SLIME_PUSTULE = register("slime_pustule", new SlimePustuleBlock(Settings.copy(Blocks.SLIME_BLOCK)), ItemGroups.NATURAL);
|
Block SLIME_PUSTULE = register("slime_pustule", new SlimePustuleBlock(Settings.copy(Blocks.SLIME_BLOCK)), ItemGroups.NATURAL);
|
||||||
|
|
||||||
Block SHAPING_BENCH = register("shaping_bench", new ShapingBenchBlock(Settings.create().mapColor(MapColor.OFF_WHITE).hardness(0.3F).resistance(0).sounds(BlockSoundGroup.WOOL)), ItemGroups.FUNCTIONAL);
|
Block SHAPING_BENCH = register("shaping_bench", new ShapingBenchBlock(Settings.create().mapColor(MapColor.OFF_WHITE).hardness(0.3F).resistance(0).sounds(BlockSoundGroup.WOOL)), ItemGroups.FUNCTIONAL);
|
||||||
Block CLOUD = register("cloud", new NaturalCloudBlock(Settings.create().mapColor(MapColor.OFF_WHITE).hardness(0.3F).resistance(0).sounds(BlockSoundGroup.WOOL), true,
|
Block CLOUD = register("cloud", new NaturalCloudBlock(true,
|
||||||
() -> UBlocks.SOGGY_CLOUD,
|
() -> UBlocks.SOGGY_CLOUD,
|
||||||
() -> UBlocks.COMPACTED_CLOUD), ItemGroups.NATURAL);
|
() -> UBlocks.COMPACTED_CLOUD,
|
||||||
|
Settings.create().mapColor(MapColor.OFF_WHITE).hardness(0.3F).resistance(0).sounds(BlockSoundGroup.WOOL)), ItemGroups.NATURAL);
|
||||||
Block COMPACTED_CLOUD = register("compacted_cloud", new CompactedCloudBlock(CLOUD.getDefaultState()));
|
Block COMPACTED_CLOUD = register("compacted_cloud", new CompactedCloudBlock(CLOUD.getDefaultState()));
|
||||||
Block CLOUD_SLAB = register("cloud_slab", new CloudSlabBlock(Settings.copy(CLOUD), true, () -> UBlocks.SOGGY_CLOUD_SLAB), ItemGroups.NATURAL);
|
Block CLOUD_SLAB = register("cloud_slab", new CloudSlabBlock(true, () -> UBlocks.SOGGY_CLOUD_SLAB, Settings.copy(CLOUD)), ItemGroups.NATURAL);
|
||||||
PoreousCloudStairsBlock CLOUD_STAIRS = register("cloud_stairs", new PoreousCloudStairsBlock(CLOUD.getDefaultState(), Settings.copy(CLOUD), () -> UBlocks.SOGGY_CLOUD_STAIRS), ItemGroups.NATURAL);
|
PoreousCloudStairsBlock CLOUD_STAIRS = register("cloud_stairs", new PoreousCloudStairsBlock(CLOUD.getDefaultState(), () -> UBlocks.SOGGY_CLOUD_STAIRS, Settings.copy(CLOUD)), ItemGroups.NATURAL);
|
||||||
|
|
||||||
Block CLOUD_PLANKS = register("cloud_planks", new NaturalCloudBlock(Settings.copy(CLOUD).hardness(0.4F).requiresTool().solid(), false,
|
Block CLOUD_PLANKS = register("cloud_planks", new NaturalCloudBlock(false, null, () -> UBlocks.COMPACTED_CLOUD_PLANKS, Settings.copy(CLOUD).hardness(0.4F).requiresTool().solid()), ItemGroups.BUILDING_BLOCKS);
|
||||||
null,
|
|
||||||
() -> UBlocks.COMPACTED_CLOUD_PLANKS), ItemGroups.BUILDING_BLOCKS);
|
|
||||||
Block COMPACTED_CLOUD_PLANKS = register("compacted_cloud_planks", new CompactedCloudBlock(CLOUD_PLANKS.getDefaultState()));
|
Block COMPACTED_CLOUD_PLANKS = register("compacted_cloud_planks", new CompactedCloudBlock(CLOUD_PLANKS.getDefaultState()));
|
||||||
Block CLOUD_PLANK_SLAB = register("cloud_plank_slab", new CloudSlabBlock(Settings.copy(CLOUD_PLANKS), false, null), ItemGroups.BUILDING_BLOCKS);
|
Block CLOUD_PLANK_SLAB = register("cloud_plank_slab", new CloudSlabBlock(false, null, Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
||||||
Block CLOUD_PLANK_STAIRS = register("cloud_plank_stairs", new CloudStairsBlock(CLOUD_PLANKS.getDefaultState(), Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
Block CLOUD_PLANK_STAIRS = register("cloud_plank_stairs", new CloudStairsBlock(CLOUD_PLANKS.getDefaultState(), Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
||||||
|
|
||||||
Block CLOUD_BRICKS = register("cloud_bricks", new NaturalCloudBlock(Settings.copy(CLOUD).hardness(0.6F).requiresTool().solid(), false,
|
Block CLOUD_BRICKS = register("cloud_bricks", new NaturalCloudBlock(false, null, () -> UBlocks.COMPACTED_CLOUD_BRICKS, Settings.copy(CLOUD).hardness(0.6F).requiresTool().solid()), ItemGroups.BUILDING_BLOCKS);
|
||||||
null,
|
|
||||||
() -> UBlocks.COMPACTED_CLOUD_BRICKS), ItemGroups.BUILDING_BLOCKS);
|
|
||||||
Block COMPACTED_CLOUD_BRICKS = register("compacted_cloud_bricks", new CompactedCloudBlock(CLOUD_BRICKS.getDefaultState()));
|
Block COMPACTED_CLOUD_BRICKS = register("compacted_cloud_bricks", new CompactedCloudBlock(CLOUD_BRICKS.getDefaultState()));
|
||||||
Block CLOUD_BRICK_SLAB = register("cloud_brick_slab", new CloudSlabBlock(Settings.copy(CLOUD_BRICKS), false, null), ItemGroups.BUILDING_BLOCKS);
|
Block CLOUD_BRICK_SLAB = register("cloud_brick_slab", new CloudSlabBlock(false, null, Settings.copy(CLOUD_BRICKS)), ItemGroups.BUILDING_BLOCKS);
|
||||||
Block CLOUD_BRICK_STAIRS = register("cloud_brick_stairs", new CloudStairsBlock(CLOUD_BRICKS.getDefaultState(), Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
Block CLOUD_BRICK_STAIRS = register("cloud_brick_stairs", new CloudStairsBlock(CLOUD_BRICKS.getDefaultState(), Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
||||||
|
|
||||||
Block ETCHED_CLOUD = register("etched_cloud", new NaturalCloudBlock(Settings.copy(CLOUD_BRICKS), false,
|
Block ETCHED_CLOUD = register("etched_cloud", new NaturalCloudBlock(false, null, () -> UBlocks.COMPACTED_CLOUD_BRICKS, Settings.copy(CLOUD_BRICKS)), ItemGroups.BUILDING_BLOCKS);
|
||||||
null,
|
|
||||||
() -> UBlocks.COMPACTED_CLOUD_BRICKS), ItemGroups.BUILDING_BLOCKS);
|
|
||||||
Block COMPACTED_ETCHED_CLOUD = register("compacted_etched_cloud", new CompactedCloudBlock(ETCHED_CLOUD.getDefaultState()));
|
Block COMPACTED_ETCHED_CLOUD = register("compacted_etched_cloud", new CompactedCloudBlock(ETCHED_CLOUD.getDefaultState()));
|
||||||
Block ETCHED_CLOUD_SLAB = register("etched_cloud_slab", new CloudSlabBlock(Settings.copy(ETCHED_CLOUD), false, null), ItemGroups.BUILDING_BLOCKS);
|
Block ETCHED_CLOUD_SLAB = register("etched_cloud_slab", new CloudSlabBlock(false, null, Settings.copy(ETCHED_CLOUD)), ItemGroups.BUILDING_BLOCKS);
|
||||||
Block ETCHED_CLOUD_STAIRS = register("etched_cloud_stairs", new CloudStairsBlock(ETCHED_CLOUD.getDefaultState(), Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
Block ETCHED_CLOUD_STAIRS = register("etched_cloud_stairs", new CloudStairsBlock(ETCHED_CLOUD.getDefaultState(), Settings.copy(CLOUD_PLANKS)), ItemGroups.BUILDING_BLOCKS);
|
||||||
|
|
||||||
SoggyCloudBlock SOGGY_CLOUD = register("soggy_cloud", new SoggyCloudBlock(Settings.copy(CLOUD).hardness(0.7F), () -> UBlocks.CLOUD));
|
SoggyCloudBlock SOGGY_CLOUD = register("soggy_cloud", new SoggyCloudBlock(() -> UBlocks.CLOUD, Settings.copy(CLOUD).hardness(0.7F)));
|
||||||
SoggyCloudSlabBlock SOGGY_CLOUD_SLAB = register("soggy_cloud_slab", new SoggyCloudSlabBlock(Settings.copy(SOGGY_CLOUD), () -> UBlocks.CLOUD_SLAB));
|
SoggyCloudSlabBlock SOGGY_CLOUD_SLAB = register("soggy_cloud_slab", new SoggyCloudSlabBlock(() -> UBlocks.CLOUD_SLAB, Settings.copy(SOGGY_CLOUD)));
|
||||||
SoggyCloudStairsBlock SOGGY_CLOUD_STAIRS = register("soggy_cloud_stairs", new SoggyCloudStairsBlock(SOGGY_CLOUD.getDefaultState(), Settings.copy(CLOUD), () -> UBlocks.CLOUD_STAIRS));
|
SoggyCloudStairsBlock SOGGY_CLOUD_STAIRS = register("soggy_cloud_stairs", new SoggyCloudStairsBlock(SOGGY_CLOUD.getDefaultState(), () -> UBlocks.CLOUD_STAIRS, Settings.copy(CLOUD)));
|
||||||
|
|
||||||
Block DENSE_CLOUD = register("dense_cloud", new NaturalCloudBlock(Settings.create().mapColor(MapColor.GRAY).hardness(0.5F).resistance(0).sounds(BlockSoundGroup.WOOL).solid(), false,
|
Block DENSE_CLOUD = register("dense_cloud", new NaturalCloudBlock(false, null, () -> UBlocks.COMPACTED_DENSE_CLOUD, Settings.create().mapColor(MapColor.GRAY).hardness(0.5F).resistance(0).sounds(BlockSoundGroup.WOOL).solid()), ItemGroups.BUILDING_BLOCKS);
|
||||||
null,
|
|
||||||
() -> UBlocks.COMPACTED_DENSE_CLOUD), ItemGroups.BUILDING_BLOCKS);
|
|
||||||
Block COMPACTED_DENSE_CLOUD = register("compacted_dense_cloud", new CompactedCloudBlock(DENSE_CLOUD.getDefaultState()));
|
Block COMPACTED_DENSE_CLOUD = register("compacted_dense_cloud", new CompactedCloudBlock(DENSE_CLOUD.getDefaultState()));
|
||||||
Block DENSE_CLOUD_SLAB = register("dense_cloud_slab", new CloudSlabBlock(Settings.copy(DENSE_CLOUD), false, null), ItemGroups.BUILDING_BLOCKS);
|
Block DENSE_CLOUD_SLAB = register("dense_cloud_slab", new CloudSlabBlock(false, null, Settings.copy(DENSE_CLOUD)), ItemGroups.BUILDING_BLOCKS);
|
||||||
Block DENSE_CLOUD_STAIRS = register("dense_cloud_stairs", new CloudStairsBlock(DENSE_CLOUD.getDefaultState(), Settings.copy(DENSE_CLOUD)), ItemGroups.BUILDING_BLOCKS);
|
Block DENSE_CLOUD_STAIRS = register("dense_cloud_stairs", new CloudStairsBlock(DENSE_CLOUD.getDefaultState(), Settings.copy(DENSE_CLOUD)), ItemGroups.BUILDING_BLOCKS);
|
||||||
|
|
||||||
Block CARVED_CLOUD = register("carved_cloud", new OrientedCloudBlock(Settings.copy(CLOUD).hardness(0.4F).requiresTool().solid(), false), ItemGroups.BUILDING_BLOCKS);
|
Block CARVED_CLOUD = register("carved_cloud", new OrientedCloudBlock(false, Settings.copy(CLOUD).hardness(0.4F).requiresTool().solid()), ItemGroups.BUILDING_BLOCKS);
|
||||||
Block UNSTABLE_CLOUD = register("unstable_cloud", new UnstableCloudBlock(Settings.copy(CLOUD)), ItemGroups.NATURAL);
|
Block UNSTABLE_CLOUD = register("unstable_cloud", new UnstableCloudBlock(Settings.copy(CLOUD)), ItemGroups.NATURAL);
|
||||||
Block CLOUD_PILLAR = register("cloud_pillar", new CloudPillarBlock(Settings.create().mapColor(MapColor.GRAY).hardness(0.5F).resistance(0).sounds(BlockSoundGroup.WOOL).solid()), ItemGroups.NATURAL);
|
Block CLOUD_PILLAR = register("cloud_pillar", new CloudPillarBlock(Settings.create().mapColor(MapColor.GRAY).hardness(0.5F).resistance(0).sounds(BlockSoundGroup.WOOL).solid()), ItemGroups.NATURAL);
|
||||||
Block CLOUD_CHEST = register("cloud_chest", new CloudChestBlock(Settings.copy(DENSE_CLOUD).instrument(Instrument.BASS).strength(2.5f), DENSE_CLOUD.getDefaultState()), ItemGroups.FUNCTIONAL);
|
Block CLOUD_CHEST = register("cloud_chest", new CloudChestBlock(DENSE_CLOUD.getDefaultState(), Settings.copy(DENSE_CLOUD).instrument(Instrument.BASS).strength(2.5f)), ItemGroups.FUNCTIONAL);
|
||||||
Block CLOTH_BED = register("cloth_bed", new FancyBedBlock("cloth", Settings.copy(Blocks.WHITE_BED).sounds(BlockSoundGroup.WOOD)));
|
Block CLOTH_BED = register("cloth_bed", new FancyBedBlock("cloth", Settings.copy(Blocks.WHITE_BED).sounds(BlockSoundGroup.WOOD)));
|
||||||
Block CLOUD_BED = register("cloud_bed", new CloudBedBlock("cloud", CLOUD.getDefaultState(), Settings.copy(Blocks.WHITE_BED).sounds(BlockSoundGroup.WOOL)));
|
Block CLOUD_BED = register("cloud_bed", new CloudBedBlock("cloud", CLOUD.getDefaultState(), Settings.copy(Blocks.WHITE_BED).sounds(BlockSoundGroup.WOOL)));
|
||||||
|
|
||||||
|
@ -209,7 +220,9 @@ public interface UBlocks {
|
||||||
Block STABLE_DOOR = register("stable_door", new StableDoorBlock(BlockSetType.OAK, Settings.copy(Blocks.OAK_DOOR)), ItemGroups.FUNCTIONAL);
|
Block STABLE_DOOR = register("stable_door", new StableDoorBlock(BlockSetType.OAK, Settings.copy(Blocks.OAK_DOOR)), ItemGroups.FUNCTIONAL);
|
||||||
Block DARK_OAK_DOOR = register("dark_oak_stable_door", new StableDoorBlock(BlockSetType.OAK, Settings.copy(Blocks.OAK_DOOR)), ItemGroups.FUNCTIONAL);
|
Block DARK_OAK_DOOR = register("dark_oak_stable_door", new StableDoorBlock(BlockSetType.OAK, Settings.copy(Blocks.OAK_DOOR)), ItemGroups.FUNCTIONAL);
|
||||||
Block CRYSTAL_DOOR = register("crystal_door", new CrystalDoorBlock(UWoodTypes.CRYSTAL, Settings.copy(Blocks.IRON_DOOR)), ItemGroups.FUNCTIONAL);
|
Block CRYSTAL_DOOR = register("crystal_door", new CrystalDoorBlock(UWoodTypes.CRYSTAL, Settings.copy(Blocks.IRON_DOOR)), ItemGroups.FUNCTIONAL);
|
||||||
Block CLOUD_DOOR = register("cloud_door", new CloudDoorBlock(Settings.copy(CLOUD), CLOUD.getDefaultState(), UWoodTypes.CLOUD), ItemGroups.FUNCTIONAL);
|
Block CLOUD_DOOR = register("cloud_door", new CloudDoorBlock(CLOUD.getDefaultState(), UWoodTypes.CLOUD, Settings.copy(CLOUD)), ItemGroups.FUNCTIONAL);
|
||||||
|
|
||||||
|
EdibleBlock HAY_BLOCK = register("hay_block", new EdibleBlock(new Identifier("hay_block"), new Identifier("wheat"), true));
|
||||||
|
|
||||||
private static <T 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);
|
||||||
|
@ -234,10 +247,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);
|
||||||
|
|
||||||
|
@ -245,21 +263,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()
|
||||||
|
|
|
@ -5,11 +5,8 @@ import com.mojang.serialization.MapCodec;
|
||||||
|
|
||||||
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 MapCodec<ZapAppleLeavesBlock> CODEC = createCodec(ZapAppleLeavesBlock::new);
|
public static final MapCodec<ZapAppleLeavesBlock> CODEC = createCodec(ZapAppleLeavesBlock::new);
|
||||||
|
@ -25,31 +22,13 @@ public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock {
|
||||||
return CODEC;
|
return CODEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) {
|
|
||||||
if (state.get(PERSISTENT)
|
|
||||||
|| oldState.isOf(state.getBlock())
|
|
||||||
|| oldState.isOf(UBlocks.ZAP_LEAVES)
|
|
||||||
|| oldState.isOf(UBlocks.FLOWERING_ZAP_LEAVES)
|
|
||||||
|| oldState.isOf(UBlocks.ZAP_LEAVES_PLACEHOLDER)
|
|
||||||
|| !(world instanceof ServerWorld sw)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ZapAppleStageStore store = ZapAppleStageStore.get(sw);
|
|
||||||
ZapAppleStageStore.Stage currentStage = store.getStage();
|
|
||||||
if (currentStage != getStage(state)) {
|
|
||||||
world.setBlockState(pos, currentStage.getNewState(state));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@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,15 +1,16 @@
|
||||||
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 com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
|
|
||||||
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 {
|
||||||
public static final MapCodec<ZapAppleLeavesPlaceholderBlock> CODEC = createCodec(ZapAppleLeavesPlaceholderBlock::new);
|
public static final MapCodec<ZapAppleLeavesPlaceholderBlock> CODEC = createCodec(ZapAppleLeavesPlaceholderBlock::new);
|
||||||
|
|
||||||
ZapAppleLeavesPlaceholderBlock(Settings settings) {
|
ZapAppleLeavesPlaceholderBlock(Settings settings) {
|
||||||
|
@ -23,37 +24,19 @@ public class ZapAppleLeavesPlaceholderBlock extends AirBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
@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,11 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
import com.minelittlepony.unicopia.block.FancyBedBlock;
|
import com.minelittlepony.unicopia.block.FancyBedBlock;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.ShapeContext;
|
import net.minecraft.block.ShapeContext;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
|
@ -20,6 +25,12 @@ import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class CloudBedBlock extends FancyBedBlock implements CloudLike {
|
public class CloudBedBlock extends FancyBedBlock implements CloudLike {
|
||||||
|
private static final MapCodec<CloudBedBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
Codec.STRING.fieldOf("base").forGetter(b -> b.base),
|
||||||
|
BlockState.CODEC.fieldOf("base_state").forGetter(b -> b.baseState),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, CloudBedBlock::new));
|
||||||
|
|
||||||
private final BlockState baseState;
|
private final BlockState baseState;
|
||||||
private final CloudBlock baseBlock;
|
private final CloudBlock baseBlock;
|
||||||
|
|
||||||
|
@ -29,6 +40,12 @@ public class CloudBedBlock extends FancyBedBlock implements CloudLike {
|
||||||
this.baseBlock = (CloudBlock)baseState.getBlock();
|
this.baseBlock = (CloudBlock)baseState.getBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
@Override
|
||||||
|
public MapCodec<BedBlock> getCodec() {
|
||||||
|
return (MapCodec)CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
public final VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||||
if (!baseBlock.canInteract(baseState, world, pos, EquineContext.of(context))) {
|
if (!baseBlock.canInteract(baseState, world, pos, EquineContext.of(context))) {
|
||||||
|
|
|
@ -4,7 +4,11 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.ShapeContext;
|
import net.minecraft.block.ShapeContext;
|
||||||
|
@ -27,13 +31,23 @@ import net.minecraft.world.LightType;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class CloudBlock extends Block implements CloudLike {
|
public class CloudBlock extends Block implements CloudLike {
|
||||||
|
private static final MapCodec<CloudBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, CloudBlock::new));
|
||||||
|
|
||||||
protected final boolean meltable;
|
protected final boolean meltable;
|
||||||
|
|
||||||
public CloudBlock(Settings settings, boolean meltable) {
|
public CloudBlock(boolean meltable, Settings settings) {
|
||||||
super((meltable ? settings.ticksRandomly() : settings).nonOpaque());
|
super((meltable ? settings.ticksRandomly() : settings).nonOpaque());
|
||||||
this.meltable = meltable;
|
this.meltable = meltable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends CloudBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEntityLand(BlockView world, Entity entity) {
|
public void onEntityLand(BlockView world, Entity entity) {
|
||||||
boolean bounce = Math.abs(entity.getVelocity().y) > 0.3;
|
boolean bounce = Math.abs(entity.getVelocity().y) > 0.3;
|
||||||
|
|
|
@ -6,10 +6,14 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
import com.minelittlepony.unicopia.block.UBlockEntities;
|
import com.minelittlepony.unicopia.block.UBlockEntities;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.ChestBlock;
|
import net.minecraft.block.ChestBlock;
|
||||||
import net.minecraft.block.DoubleBlockProperties;
|
import net.minecraft.block.DoubleBlockProperties;
|
||||||
import net.minecraft.block.ShapeContext;
|
import net.minecraft.block.ShapeContext;
|
||||||
|
import net.minecraft.block.StairsBlock;
|
||||||
import net.minecraft.block.entity.BlockEntity;
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
import net.minecraft.block.entity.BlockEntityType;
|
import net.minecraft.block.entity.BlockEntityType;
|
||||||
import net.minecraft.block.entity.ChestBlockEntity;
|
import net.minecraft.block.entity.ChestBlockEntity;
|
||||||
|
@ -33,6 +37,10 @@ import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class CloudChestBlock extends ChestBlock implements CloudLike {
|
public class CloudChestBlock extends ChestBlock implements CloudLike {
|
||||||
|
private static final MapCodec<CloudChestBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseState),
|
||||||
|
StairsBlock.createSettingsCodec()
|
||||||
|
).apply(instance, CloudChestBlock::new));
|
||||||
private final BlockState baseState;
|
private final BlockState baseState;
|
||||||
private final CloudBlock baseBlock;
|
private final CloudBlock baseBlock;
|
||||||
|
|
||||||
|
@ -76,12 +84,17 @@ public class CloudChestBlock extends ChestBlock implements CloudLike {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public CloudChestBlock(Settings settings, BlockState baseState) {
|
public CloudChestBlock(BlockState baseState, Settings settings) {
|
||||||
super(settings, () -> UBlockEntities.CLOUD_CHEST);
|
super(settings, () -> UBlockEntities.CLOUD_CHEST);
|
||||||
this.baseState = baseState;
|
this.baseState = baseState;
|
||||||
this.baseBlock = (CloudBlock)baseState.getBlock();
|
this.baseBlock = (CloudBlock)baseState.getBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends ChestBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||||
return new TileData(pos, state);
|
return new TileData(pos, state);
|
||||||
|
|
|
@ -4,6 +4,8 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
import com.minelittlepony.unicopia.Race;
|
import com.minelittlepony.unicopia.Race;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
import net.minecraft.block.BlockSetType;
|
import net.minecraft.block.BlockSetType;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
@ -22,15 +24,24 @@ import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class CloudDoorBlock extends DoorBlock implements CloudLike {
|
public class CloudDoorBlock extends DoorBlock implements CloudLike {
|
||||||
|
private static final MapCodec<CloudDoorBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
BlockState.CODEC.fieldOf("base_state").forGetter(b -> b.baseState),
|
||||||
|
BlockSetType.CODEC.fieldOf("block_set_type").forGetter(DoorBlock::getBlockSetType),
|
||||||
|
DoorBlock.createSettingsCodec()
|
||||||
|
).apply(instance, CloudDoorBlock::new));
|
||||||
private final BlockState baseState;
|
private final BlockState baseState;
|
||||||
private final CloudBlock baseBlock;
|
private final CloudBlock baseBlock;
|
||||||
|
|
||||||
public CloudDoorBlock(Settings settings, BlockState baseState, BlockSetType blockSet) {
|
public CloudDoorBlock(BlockState baseState, BlockSetType blockSet, Settings settings) {
|
||||||
super(blockSet, settings);
|
super(blockSet, settings);
|
||||||
this.baseState = baseState;
|
this.baseState = baseState;
|
||||||
this.baseBlock = (CloudBlock)baseState.getBlock();
|
this.baseBlock = (CloudBlock)baseState.getBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends CloudDoorBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
public final VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import java.util.Map;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
@ -20,6 +21,7 @@ import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.WorldAccess;
|
import net.minecraft.world.WorldAccess;
|
||||||
|
|
||||||
public class CloudPillarBlock extends CloudBlock {
|
public class CloudPillarBlock extends CloudBlock {
|
||||||
|
private static final MapCodec<CloudPillarBlock> CODEC = Block.createCodec(CloudPillarBlock::new);
|
||||||
private static final BooleanProperty NORTH = BooleanProperty.of("north");
|
private static final BooleanProperty NORTH = BooleanProperty.of("north");
|
||||||
private static final BooleanProperty SOUTH = BooleanProperty.of("south");
|
private static final BooleanProperty SOUTH = BooleanProperty.of("south");
|
||||||
private static final Map<Direction, BooleanProperty> DIRECTION_PROPERTIES = Map.of(
|
private static final Map<Direction, BooleanProperty> DIRECTION_PROPERTIES = Map.of(
|
||||||
|
@ -41,10 +43,15 @@ public class CloudPillarBlock extends CloudBlock {
|
||||||
// [1,0] [1,1]
|
// [1,0] [1,1]
|
||||||
|
|
||||||
public CloudPillarBlock(Settings settings) {
|
public CloudPillarBlock(Settings settings) {
|
||||||
super(settings, false);
|
super(false, settings);
|
||||||
setDefaultState(getDefaultState().with(NORTH, true).with(SOUTH, true));
|
setDefaultState(getDefaultState().with(NORTH, true).with(SOUTH, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<CloudPillarBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
builder.add(NORTH, SOUTH);
|
builder.add(NORTH, SOUTH);
|
||||||
|
|
|
@ -5,7 +5,12 @@ import java.util.function.Supplier;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
|
import com.minelittlepony.unicopia.util.CodecUtils;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.ShapeContext;
|
import net.minecraft.block.ShapeContext;
|
||||||
|
@ -24,14 +29,24 @@ import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.WorldAccess;
|
import net.minecraft.world.WorldAccess;
|
||||||
|
|
||||||
public class CloudSlabBlock extends WaterloggableCloudBlock {
|
public class CloudSlabBlock extends WaterloggableCloudBlock {
|
||||||
|
private static final MapCodec<WaterloggableCloudBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable),
|
||||||
|
CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, WaterloggableCloudBlock::new));
|
||||||
private static final VoxelShape BOTTOM_SHAPE = Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 8.0, 16.0);
|
private static final VoxelShape BOTTOM_SHAPE = Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 8.0, 16.0);
|
||||||
private static final VoxelShape TOP_SHAPE = Block.createCuboidShape(0.0, 8.0, 0.0, 16.0, 16.0, 16.0);
|
private static final VoxelShape TOP_SHAPE = Block.createCuboidShape(0.0, 8.0, 0.0, 16.0, 16.0, 16.0);
|
||||||
|
|
||||||
public CloudSlabBlock(Settings settings, boolean meltable, @Nullable Supplier<Soakable> soggyBlock) {
|
public CloudSlabBlock(boolean meltable, @Nullable Supplier<Soakable> soggyBlock, Settings settings) {
|
||||||
super(settings, meltable, soggyBlock);
|
super(meltable, soggyBlock, settings);
|
||||||
setDefaultState(getDefaultState().with(SlabBlock.TYPE, SlabType.BOTTOM));
|
setDefaultState(getDefaultState().with(SlabBlock.TYPE, SlabType.BOTTOM));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends WaterloggableCloudBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasSidedTransparency(BlockState state) {
|
public boolean hasSidedTransparency(BlockState state) {
|
||||||
return state.get(SlabBlock.TYPE) != SlabType.DOUBLE;
|
return state.get(SlabBlock.TYPE) != SlabType.DOUBLE;
|
||||||
|
|
|
@ -3,6 +3,9 @@ package com.minelittlepony.unicopia.block.cloud;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.ShapeContext;
|
import net.minecraft.block.ShapeContext;
|
||||||
import net.minecraft.block.StairsBlock;
|
import net.minecraft.block.StairsBlock;
|
||||||
|
@ -17,6 +20,10 @@ import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class CloudStairsBlock extends StairsBlock implements CloudLike {
|
public class CloudStairsBlock extends StairsBlock implements CloudLike {
|
||||||
|
private static final MapCodec<CloudStairsBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseBlockState),
|
||||||
|
StairsBlock.createSettingsCodec()
|
||||||
|
).apply(instance, CloudStairsBlock::new));
|
||||||
|
|
||||||
private final CloudBlock baseBlock;
|
private final CloudBlock baseBlock;
|
||||||
|
|
||||||
|
@ -25,6 +32,11 @@ public class CloudStairsBlock extends StairsBlock implements CloudLike {
|
||||||
this.baseBlock = (CloudBlock)baseState.getBlock();
|
this.baseBlock = (CloudBlock)baseState.getBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends StairsBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEntityLand(BlockView world, Entity entity) {
|
public void onEntityLand(BlockView world, Entity entity) {
|
||||||
baseBlock.onEntityLand(world, entity);
|
baseBlock.onEntityLand(world, entity);
|
||||||
|
|
|
@ -5,6 +5,8 @@ import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
@ -32,6 +34,9 @@ import net.minecraft.world.World;
|
||||||
import net.minecraft.world.WorldView;
|
import net.minecraft.world.WorldView;
|
||||||
|
|
||||||
public class CompactedCloudBlock extends CloudBlock {
|
public class CompactedCloudBlock extends CloudBlock {
|
||||||
|
private static final MapCodec<CompactedCloudBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseState)
|
||||||
|
).apply(instance, CompactedCloudBlock::new));
|
||||||
static final Map<Direction, BooleanProperty> FACING_PROPERTIES = ConnectingBlock.FACING_PROPERTIES;
|
static final Map<Direction, BooleanProperty> FACING_PROPERTIES = ConnectingBlock.FACING_PROPERTIES;
|
||||||
static final Collection<BooleanProperty> PROPERTIES = FACING_PROPERTIES.values();
|
static final Collection<BooleanProperty> PROPERTIES = FACING_PROPERTIES.values();
|
||||||
|
|
||||||
|
@ -49,13 +54,18 @@ public class CompactedCloudBlock extends CloudBlock {
|
||||||
private final BlockState baseState;
|
private final BlockState baseState;
|
||||||
|
|
||||||
public CompactedCloudBlock(BlockState baseState) {
|
public CompactedCloudBlock(BlockState baseState) {
|
||||||
super(Settings.copy(baseState.getBlock()).dropsLike(baseState.getBlock()), true);
|
super(true, Settings.copy(baseState.getBlock()).dropsLike(baseState.getBlock()));
|
||||||
this.baseState = baseState;
|
this.baseState = baseState;
|
||||||
PROPERTIES.forEach(property -> {
|
PROPERTIES.forEach(property -> {
|
||||||
setDefaultState(getDefaultState().with(property, true));
|
setDefaultState(getDefaultState().with(property, true));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<CompactedCloudBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack getPickStack(WorldView world, BlockPos pos, BlockState state) {
|
public ItemStack getPickStack(WorldView world, BlockPos pos, BlockState state) {
|
||||||
return baseState.getBlock().getPickStack(world, pos, baseState);
|
return baseState.getBlock().getPickStack(world, pos, baseState);
|
||||||
|
|
|
@ -4,10 +4,17 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.util.CodecUtils;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
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.registry.Registries;
|
||||||
import net.minecraft.registry.tag.ItemTags;
|
import net.minecraft.registry.tag.ItemTags;
|
||||||
import net.minecraft.sound.SoundCategory;
|
import net.minecraft.sound.SoundCategory;
|
||||||
import net.minecraft.sound.SoundEvents;
|
import net.minecraft.sound.SoundEvents;
|
||||||
|
@ -19,16 +26,28 @@ import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class NaturalCloudBlock extends PoreousCloudBlock {
|
public class NaturalCloudBlock extends PoreousCloudBlock {
|
||||||
|
private static final MapCodec<NaturalCloudBlock> CODEC = RecordCodecBuilder.<NaturalCloudBlock>mapCodec(instance -> instance.group(
|
||||||
|
Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable),
|
||||||
|
CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock),
|
||||||
|
CodecUtils.supplierOf(Registries.BLOCK.getCodec()).fieldOf("compacted_block").forGetter(b -> b.compactedBlock),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, NaturalCloudBlock::new));
|
||||||
|
|
||||||
private final Supplier<Block> compactedBlock;
|
private final Supplier<Block> compactedBlock;
|
||||||
|
|
||||||
public NaturalCloudBlock(Settings settings, boolean meltable,
|
public NaturalCloudBlock(boolean meltable,
|
||||||
@Nullable Supplier<Soakable> soggyBlock,
|
@Nullable Supplier<Soakable> soggyBlock,
|
||||||
Supplier<Block> compactedBlock) {
|
Supplier<Block> compactedBlock,
|
||||||
super(settings.nonOpaque(), meltable, soggyBlock);
|
Settings settings) {
|
||||||
|
super(meltable, soggyBlock, settings.nonOpaque());
|
||||||
this.compactedBlock = compactedBlock;
|
this.compactedBlock = compactedBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<NaturalCloudBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||||
ItemStack stack = player.getStackInHand(hand);
|
ItemStack stack = player.getStackInHand(hand);
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
package com.minelittlepony.unicopia.block.cloud;
|
package com.minelittlepony.unicopia.block.cloud;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.item.ItemPlacementContext;
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
|
@ -13,13 +17,22 @@ import net.minecraft.util.BlockRotation;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
|
|
||||||
public class OrientedCloudBlock extends CloudBlock {
|
public class OrientedCloudBlock extends CloudBlock {
|
||||||
|
private static final MapCodec<OrientedCloudBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, OrientedCloudBlock::new));
|
||||||
public static final DirectionProperty FACING = Properties.FACING;
|
public static final DirectionProperty FACING = Properties.FACING;
|
||||||
|
|
||||||
public OrientedCloudBlock(Settings settings, boolean meltable) {
|
public OrientedCloudBlock(boolean meltable, Settings settings) {
|
||||||
super(settings, meltable);
|
super(meltable, settings);
|
||||||
this.setDefaultState(getDefaultState().with(FACING, Direction.UP));
|
this.setDefaultState(getDefaultState().with(FACING, Direction.UP));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends CloudBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
builder.add(FACING);
|
builder.add(FACING);
|
||||||
|
|
|
@ -4,25 +4,43 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
import com.minelittlepony.unicopia.util.CodecUtils;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.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;
|
||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
|
|
||||||
public class PoreousCloudBlock extends CloudBlock implements Soakable {
|
public class PoreousCloudBlock extends CloudBlock implements Soakable {
|
||||||
@Nullable
|
private static final MapCodec<PoreousCloudBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
private final Supplier<Soakable> soggyBlock;
|
Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable),
|
||||||
|
CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, PoreousCloudBlock::new));
|
||||||
|
|
||||||
public PoreousCloudBlock(Settings settings, boolean meltable, @Nullable Supplier<Soakable> soggyBlock) {
|
@Nullable
|
||||||
super(settings.nonOpaque(), meltable);
|
protected final Supplier<Soakable> soggyBlock;
|
||||||
|
|
||||||
|
public PoreousCloudBlock(boolean meltable, @Nullable Supplier<Soakable> soggyBlock, Settings settings) {
|
||||||
|
super(meltable, settings.nonOpaque());
|
||||||
this.soggyBlock = soggyBlock;
|
this.soggyBlock = soggyBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends PoreousCloudBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@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,22 +4,38 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
import com.minelittlepony.unicopia.util.CodecUtils;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.StairsBlock;
|
||||||
|
|
||||||
public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakable {
|
public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakable {
|
||||||
|
private static final MapCodec<PoreousCloudStairsBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseBlockState),
|
||||||
|
CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock),
|
||||||
|
StairsBlock.createSettingsCodec()
|
||||||
|
).apply(instance, PoreousCloudStairsBlock::new));
|
||||||
|
|
||||||
protected final Supplier<Soakable> soggyBlock;
|
protected final Supplier<Soakable> soggyBlock;
|
||||||
|
|
||||||
public PoreousCloudStairsBlock(BlockState baseState, Settings settings, Supplier<Soakable> soggyBlock) {
|
public PoreousCloudStairsBlock(BlockState baseState, Supplier<Soakable> soggyBlock, Settings settings) {
|
||||||
super(baseState, settings);
|
super(baseState, settings);
|
||||||
this.soggyBlock = soggyBlock;
|
this.soggyBlock = soggyBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends PoreousCloudStairsBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
import com.minelittlepony.unicopia.container.ShapingBenchScreenHandler;
|
import com.minelittlepony.unicopia.container.ShapingBenchScreenHandler;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
@ -23,6 +24,7 @@ import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class ShapingBenchBlock extends CloudBlock {
|
public class ShapingBenchBlock extends CloudBlock {
|
||||||
|
private static final MapCodec<ShapingBenchBlock> CODEC = Block.createCodec(ShapingBenchBlock::new);
|
||||||
private static final VoxelShape SHAPE = VoxelShapes.union(
|
private static final VoxelShape SHAPE = VoxelShapes.union(
|
||||||
Block.createCuboidShape(0, 13, 0, 3, 18, 3),
|
Block.createCuboidShape(0, 13, 0, 3, 18, 3),
|
||||||
Block.createCuboidShape(13, 13, 0, 16, 18, 3),
|
Block.createCuboidShape(13, 13, 0, 16, 18, 3),
|
||||||
|
@ -34,7 +36,12 @@ public class ShapingBenchBlock extends CloudBlock {
|
||||||
);
|
);
|
||||||
|
|
||||||
public ShapingBenchBlock(Settings settings) {
|
public ShapingBenchBlock(Settings settings) {
|
||||||
super(settings, false);
|
super(false, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<ShapingBenchBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
package com.minelittlepony.unicopia.block.cloud;
|
package com.minelittlepony.unicopia.block.cloud;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.USounds;
|
import com.minelittlepony.unicopia.USounds;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
|
||||||
|
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;
|
||||||
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.particle.ParticleTypes;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
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;
|
||||||
|
@ -27,6 +28,8 @@ import net.minecraft.world.World;
|
||||||
import net.minecraft.world.event.GameEvent;
|
import net.minecraft.world.event.GameEvent;
|
||||||
|
|
||||||
public interface Soakable {
|
public interface Soakable {
|
||||||
|
Codec<Soakable> CODEC = Registries.BLOCK.getCodec().xmap(b -> (Soakable)b, s -> (Block)s);
|
||||||
|
|
||||||
IntProperty MOISTURE = IntProperty.of("moisture", 1, 7);
|
IntProperty MOISTURE = IntProperty.of("moisture", 1, 7);
|
||||||
Direction[] DIRECTIONS = Arrays.stream(Direction.values()).filter(d -> d != Direction.UP).toArray(Direction[]::new);
|
Direction[] DIRECTIONS = Arrays.stream(Direction.values()).filter(d -> d != Direction.UP).toArray(Direction[]::new);
|
||||||
|
|
||||||
|
@ -105,15 +108,4 @@ public interface Soakable {
|
||||||
world.setBlockState(pos, soakable.getStateWithMoisture(state, newMoisture));
|
world.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,10 +4,17 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
import com.minelittlepony.unicopia.util.CodecUtils;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
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.registry.Registries;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.state.StateManager;
|
import net.minecraft.state.StateManager;
|
||||||
import net.minecraft.util.ActionResult;
|
import net.minecraft.util.ActionResult;
|
||||||
|
@ -19,15 +26,24 @@ import net.minecraft.world.World;
|
||||||
import net.minecraft.world.WorldView;
|
import net.minecraft.world.WorldView;
|
||||||
|
|
||||||
public class SoggyCloudBlock extends CloudBlock implements Soakable {
|
public class SoggyCloudBlock extends CloudBlock implements Soakable {
|
||||||
|
private static final MapCodec<SoggyCloudBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
CodecUtils.supplierOf(Registries.BLOCK.getCodec()).fieldOf("dry_block").forGetter(b -> b.dryBlock),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, SoggyCloudBlock::new));
|
||||||
|
|
||||||
private final Supplier<Block> dryBlock;
|
private final Supplier<Block> dryBlock;
|
||||||
|
|
||||||
public SoggyCloudBlock(Settings settings, Supplier<Block> dryBlock) {
|
public SoggyCloudBlock(Supplier<Block> dryBlock, Settings settings) {
|
||||||
super(settings.ticksRandomly(), false);
|
super(false, settings.ticksRandomly());
|
||||||
setDefaultState(getDefaultState().with(MOISTURE, 7));
|
setDefaultState(getDefaultState().with(MOISTURE, 7));
|
||||||
this.dryBlock = dryBlock;
|
this.dryBlock = dryBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends SoggyCloudBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
super.appendProperties(builder);
|
super.appendProperties(builder);
|
||||||
|
@ -43,9 +59,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,10 +4,17 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
import com.minelittlepony.unicopia.util.CodecUtils;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
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.registry.Registries;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.state.StateManager;
|
import net.minecraft.state.StateManager;
|
||||||
import net.minecraft.util.ActionResult;
|
import net.minecraft.util.ActionResult;
|
||||||
|
@ -19,15 +26,24 @@ import net.minecraft.world.World;
|
||||||
import net.minecraft.world.WorldView;
|
import net.minecraft.world.WorldView;
|
||||||
|
|
||||||
public class SoggyCloudSlabBlock extends CloudSlabBlock {
|
public class SoggyCloudSlabBlock extends CloudSlabBlock {
|
||||||
|
private static final MapCodec<SoggyCloudSlabBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
CodecUtils.supplierOf(Registries.BLOCK.getCodec()).fieldOf("dry_block").forGetter(b -> b.dryBlock),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, SoggyCloudSlabBlock::new));
|
||||||
|
|
||||||
private final Supplier<Block> dryBlock;
|
private final Supplier<Block> dryBlock;
|
||||||
|
|
||||||
public SoggyCloudSlabBlock(Settings settings, Supplier<Block> dryBlock) {
|
public SoggyCloudSlabBlock(Supplier<Block> dryBlock, Settings settings) {
|
||||||
super(settings.ticksRandomly(), false, null);
|
super(false, null, settings.ticksRandomly());
|
||||||
setDefaultState(getDefaultState().with(MOISTURE, 7));
|
setDefaultState(getDefaultState().with(MOISTURE, 7));
|
||||||
this.dryBlock = dryBlock;
|
this.dryBlock = dryBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends SoggyCloudSlabBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
super.appendProperties(builder);
|
super.appendProperties(builder);
|
||||||
|
@ -43,9 +59,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,23 +4,40 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||||
|
import com.minelittlepony.unicopia.util.CodecUtils;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.StairsBlock;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
import net.minecraft.state.StateManager;
|
import net.minecraft.state.StateManager;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.WorldView;
|
import net.minecraft.world.WorldView;
|
||||||
|
|
||||||
public class SoggyCloudStairsBlock extends CloudStairsBlock implements Soakable {
|
public class SoggyCloudStairsBlock extends CloudStairsBlock implements Soakable {
|
||||||
|
private static final MapCodec<SoggyCloudStairsBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
BlockState.CODEC.fieldOf("base_state").forGetter(block -> block.baseBlockState),
|
||||||
|
CodecUtils.supplierOf(Registries.BLOCK.getCodec()).optionalFieldOf("soggy_block", null).forGetter(b -> b.dryBlock),
|
||||||
|
StairsBlock.createSettingsCodec()
|
||||||
|
).apply(instance, SoggyCloudStairsBlock::new));
|
||||||
|
|
||||||
private final Supplier<Block> dryBlock;
|
private final Supplier<Block> dryBlock;
|
||||||
|
|
||||||
public SoggyCloudStairsBlock(BlockState baseState, Settings settings, Supplier<Block> dryBlock) {
|
public SoggyCloudStairsBlock(BlockState baseState, Supplier<Block> dryBlock, Settings settings) {
|
||||||
super(baseState, settings);
|
super(baseState, settings);
|
||||||
setDefaultState(getDefaultState().with(MOISTURE, 7));
|
setDefaultState(getDefaultState().with(MOISTURE, 7));
|
||||||
this.dryBlock = dryBlock;
|
this.dryBlock = dryBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends SoggyCloudStairsBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
super.appendProperties(builder);
|
super.appendProperties(builder);
|
||||||
|
@ -36,8 +53,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import java.util.Optional;
|
||||||
import com.minelittlepony.unicopia.entity.mob.StormCloudEntity;
|
import com.minelittlepony.unicopia.entity.mob.StormCloudEntity;
|
||||||
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.mojang.serialization.MapCodec;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
@ -28,14 +29,20 @@ import net.minecraft.world.poi.PointOfInterestStorage;
|
||||||
import net.minecraft.world.poi.PointOfInterestTypes;
|
import net.minecraft.world.poi.PointOfInterestTypes;
|
||||||
|
|
||||||
public class UnstableCloudBlock extends CloudBlock {
|
public class UnstableCloudBlock extends CloudBlock {
|
||||||
|
private static final MapCodec<UnstableCloudBlock> CODEC = Block.createCodec(UnstableCloudBlock::new);
|
||||||
private static final int MAX_CHARGE = 6;
|
private static final int MAX_CHARGE = 6;
|
||||||
private static final IntProperty CHARGE = IntProperty.of("charge", 0, MAX_CHARGE);
|
private static final IntProperty CHARGE = IntProperty.of("charge", 0, MAX_CHARGE);
|
||||||
|
|
||||||
public UnstableCloudBlock(Settings settings) {
|
public UnstableCloudBlock(Settings settings) {
|
||||||
super(settings, false);
|
super(false, settings);
|
||||||
setDefaultState(getDefaultState().with(CHARGE, 0));
|
setDefaultState(getDefaultState().with(CHARGE, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<UnstableCloudBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
builder.add(CHARGE);
|
builder.add(CHARGE);
|
||||||
|
|
|
@ -5,7 +5,12 @@ import java.util.function.Supplier;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquineContext;
|
import com.minelittlepony.unicopia.EquineContext;
|
||||||
|
import com.minelittlepony.unicopia.util.CodecUtils;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.block.BedBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Waterloggable;
|
import net.minecraft.block.Waterloggable;
|
||||||
|
@ -23,13 +28,24 @@ import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.WorldAccess;
|
import net.minecraft.world.WorldAccess;
|
||||||
|
|
||||||
public class WaterloggableCloudBlock extends PoreousCloudBlock implements Waterloggable {
|
public class WaterloggableCloudBlock extends PoreousCloudBlock implements Waterloggable {
|
||||||
|
private static final MapCodec<WaterloggableCloudBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
Codec.BOOL.fieldOf("meltable").forGetter(b -> b.meltable),
|
||||||
|
CodecUtils.supplierOf(Soakable.CODEC).optionalFieldOf("soggy_block", null).forGetter(b -> b.soggyBlock),
|
||||||
|
BedBlock.createSettingsCodec()
|
||||||
|
).apply(instance, WaterloggableCloudBlock::new));
|
||||||
|
|
||||||
public static final BooleanProperty WATERLOGGED = Properties.WATERLOGGED;
|
public static final BooleanProperty WATERLOGGED = Properties.WATERLOGGED;
|
||||||
|
|
||||||
public WaterloggableCloudBlock(Settings settings, boolean meltable, @Nullable Supplier<Soakable> soggyBlock) {
|
public WaterloggableCloudBlock(boolean meltable, @Nullable Supplier<Soakable> soggyBlock, Settings settings) {
|
||||||
super(settings, meltable, soggyBlock);
|
super(meltable, soggyBlock, settings);
|
||||||
setDefaultState(getDefaultState().with(WATERLOGGED, false));
|
setDefaultState(getDefaultState().with(WATERLOGGED, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends WaterloggableCloudBlock> getCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
builder.add(WATERLOGGED);
|
builder.add(WATERLOGGED);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue