-
Notifications
You must be signed in to change notification settings - Fork 18
/
SmashingAnvilEntity.java
209 lines (186 loc) · 6.54 KB
/
SmashingAnvilEntity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package knightminer.inspirations.recipes.entity;
import knightminer.inspirations.library.InspirationsRegistry;
import knightminer.inspirations.library.recipe.anvil.AnvilInventory;
import knightminer.inspirations.library.recipe.anvil.AnvilRecipe;
import knightminer.inspirations.recipes.InspirationsRecipes;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.FallingBlock;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.MoverType;
import net.minecraft.entity.item.FallingBlockEntity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.DirectionalPlaceContext;
import net.minecraft.item.ItemStack;
import net.minecraft.network.IPacket;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameRules;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
import net.minecraftforge.fml.network.NetworkHooks;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.stream.Collectors;
public class SmashingAnvilEntity extends FallingBlockEntity implements IEntityAdditionalSpawnData {
public SmashingAnvilEntity(EntityType<SmashingAnvilEntity> entityType, World world) {
super(entityType, world);
}
public SmashingAnvilEntity(World world, double x, double y, double z, BlockState fallingBlockState) {
super(InspirationsRecipes.smashingAnvil, world);
this.fallTile = fallingBlockState;
this.preventEntitySpawning = true;
this.setPosition(x, y, z);
this.setMotion(Vec3d.ZERO);
this.prevPosX = x;
this.prevPosY = y;
this.prevPosZ = z;
this.setOrigin(new BlockPos(this));
// From anvil:
setHurtEntities(true);
}
@Nonnull
@Override
public IPacket<?> createSpawnPacket() {
return NetworkHooks.getEntitySpawningPacket(this);
}
// We can't use vanilla's spawn data method, so implement it here.
@Override
public void writeSpawnData(PacketBuffer buffer) {
buffer.writeInt(Block.getStateId(fallTile));
}
@Override
public void readSpawnData(PacketBuffer additionalData) {
fallTile = Block.getStateById(additionalData.readInt());
}
// Copy most of the original wholesale, changing some things.
@Override
public void tick() {
if(this.fallTile.isAir()) {
this.remove();
}
Block block = this.fallTile.getBlock();
if(fallTime++ == 0) {
BlockPos blockpos = new BlockPos(this);
if(world.getBlockState(blockpos).getBlock() == block) {
world.removeBlock(blockpos, false);
} else if(!world.isRemote) {
this.remove();
return;
}
}
if(!hasNoGravity()) {
setMotion(this.getMotion().add(0.0D, -0.04D, 0.0D));
}
Vec3d motion = this.getMotion();
move(MoverType.SELF, motion);
if(!world.isRemote) {
BlockPos blockpos = new BlockPos(this);
if(!onGround) {
if(fallTime > 100 && (blockpos.getY() < 1 || blockpos.getY() > 256) || fallTime > 600) {
tryDropItem();
remove();
}
} else {
// On the ground, place the block.
this.setMotion(getMotion().mul(0.7D, -0.5D, 0.7D));
if(dontSetBlock) { // It's a destroyed anvil, play sounds.
if(block instanceof FallingBlock) {
((FallingBlock) block).onBroken(this.world, blockpos);
}
remove();
} else {
BlockPos below = blockpos.down();
// Original behaviour - only placed if replaceable && canBeHere.
// Instead, if we destroy the block retain the entity.
if(smashBlock(world, below, world.getBlockState(below)) == SmashResult.PASSTHROUGH) {
// Restore velocity before the move() call, to preserve momentum.
this.setMotion(motion);
} else {
placeAnvil(blockpos);
}
}
}
}
this.setMotion(this.getMotion().scale(0.98D));
}
/**
* We landed, set the block if possible otherwise drop the item.
* This follows vanilla's rules for how it can be placed.
* */
private void placeAnvil(BlockPos blockpos) {
BlockState blockstate = world.getBlockState(blockpos);
boolean replaceable = blockstate.isReplaceable(new DirectionalPlaceContext(world, blockpos, Direction.DOWN, ItemStack.EMPTY, Direction.UP));
boolean canFallThrough = FallingBlock.canFallThrough(world.getBlockState(blockpos.down()));
boolean canBeHere = this.fallTile.isValidPosition(this.world, blockpos) && !canFallThrough;
if(replaceable && canBeHere) {
if(!world.setBlockState(blockpos, this.fallTile, 3)) {
tryDropItem();
}
} else {
tryDropItem();
}
remove();
}
/* If allowed, drop our item right here. */
private void tryDropItem() {
if(this.shouldDropItem && this.world.getGameRules().getBoolean(GameRules.DO_ENTITY_DROPS)) {
this.entityDropItem(this.fallTile.getBlock());
}
}
/**
* Smashes the provided block by anvil.
* @param pos The position of the block.
* @param state The existing block at the position.
* @param world The world the block is in.
* @return The result of the operation.
*/
public static SmashResult smashBlock(World world, BlockPos pos, BlockState state) {
// Always pass harmlessly through air.
if(state.getBlock().isAir(state, world, pos)) {
return SmashResult.PASSTHROUGH;
}
// If the block is unbreakable, always fail.
if(state.getBlockHardness(world, pos) == -1) {
return SmashResult.FAIL;
}
// Find all the items on this block, plus the one above (where the anvil is).
List<ItemEntity> items = world.getEntitiesWithinAABB(ItemEntity.class, new AxisAlignedBB(
pos.getX(), pos.getY() + 0.5, pos.getZ(),
pos.getX() + 1, pos.getY() + 2, pos.getZ() + 1)
);
// Dummy inventory, used to pass the items/state to the recipes.
AnvilInventory inv = new AnvilInventory(
items.stream().map(ItemEntity::getItem).collect(Collectors.toList()),
state
);
return world.getRecipeManager()
.getRecipe(InspirationsRegistry.ANVIL_RECIPE_TYPE, inv, world)
.map((recipe) -> {
BlockState transformation = recipe.getBlockResult(inv);
recipe.consumeItemEnts(items);
// if the result is air, break the block
if(transformation.getBlock() == Blocks.AIR) {
world.destroyBlock(pos, true);
return SmashResult.PASSTHROUGH;
} else {
// breaking particles
world.playEvent(2001, pos, Block.getStateId(state));
world.setBlockState(pos, transformation);
return SmashResult.TRANSFORM;
}
}).orElse(SmashResult.FAIL);
}
enum SmashResult {
/** Broke the block, fall through it. */
PASSTHROUGH,
/** No recipe found. */
FAIL,
/** Converted the block. */
TRANSFORM
}
}