Skip to content

Commit 4545e4d

Browse files
committed
脚手架现在支持机械动力的放置预览
1 parent 688839b commit 4545e4d

6 files changed

Lines changed: 218 additions & 22 deletions

File tree

src/main/java/com/teammoeg/frostedheart/bootstrap/common/FHBlocks.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1952,7 +1952,8 @@ private static boolean never(BlockState state, BlockGetter getter, BlockPos pos)
19521952
.initialProperties(METAL_SCAFFOLDING)
19531953
.tag(BlockTags.MINEABLE_WITH_PICKAXE, BlockTags.NEEDS_STONE_TOOL)
19541954
.blockstate(existed())
1955-
.simpleItem()
1955+
.item(FHScaffoldingStairBlockItem::new)
1956+
.build()
19561957
.register();
19571958

19581959
public static final BlockEntry<WarehouseStorageRackBlock> WAREHOUSE_STORAGE_RACK = REGISTRATE.block("warehouse_storage_rack", WarehouseStorageRackBlock::new)

src/main/java/com/teammoeg/frostedheart/content/decoration/FHScaffoldingBlock.java

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package com.teammoeg.frostedheart.content.decoration;
22

3+
import com.simibubi.create.content.equipment.extendoGrip.ExtendoGripItem;
4+
import com.simibubi.create.foundation.placement.IPlacementHelper;
5+
import com.simibubi.create.foundation.placement.PlacementHelpers;
6+
import com.simibubi.create.foundation.placement.PlacementOffset;
7+
import com.simibubi.create.infrastructure.config.AllConfigs;
38
import com.teammoeg.chorda.block.CBlock;
49
import com.teammoeg.chorda.util.struct.FastEnumMap;
510
import com.teammoeg.frostedheart.bootstrap.common.FHBlocks;
@@ -10,7 +15,10 @@
1015
import net.minecraft.sounds.SoundSource;
1116
import net.minecraft.world.InteractionHand;
1217
import net.minecraft.world.InteractionResult;
18+
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
1319
import net.minecraft.world.entity.player.Player;
20+
import net.minecraft.world.item.BlockItem;
21+
import net.minecraft.world.item.ItemStack;
1422
import net.minecraft.world.item.context.BlockPlaceContext;
1523
import net.minecraft.world.level.BlockGetter;
1624
import net.minecraft.world.level.Level;
@@ -27,13 +35,15 @@
2735
import net.minecraft.world.phys.shapes.CollisionContext;
2836
import net.minecraft.world.phys.shapes.Shapes;
2937
import net.minecraft.world.phys.shapes.VoxelShape;
38+
import net.minecraftforge.common.ForgeMod;
3039
import net.minecraftforge.registries.ForgeRegistries;
3140
import org.jetbrains.annotations.NotNull;
3241
import org.jetbrains.annotations.Nullable;
3342

3443
import java.util.ArrayList;
3544
import java.util.List;
3645
import java.util.concurrent.ConcurrentHashMap;
46+
import java.util.function.Predicate;
3747

3848
public class FHScaffoldingBlock extends CBlock implements SimpleWaterloggedBlock {
3949
public static final BooleanProperty SURFACE = BooleanProperty.create("surface");
@@ -60,6 +70,8 @@ public class FHScaffoldingBlock extends CBlock implements SimpleWaterloggedBlock
6070

6171
private static final ConcurrentHashMap<BlockState, VoxelShape> shapeCache = new ConcurrentHashMap<>();
6272

73+
public static final int PLACEMENT_HELPER_ID = PlacementHelpers.register(new ScaffoldingPlacementHelper());
74+
6375
public FHScaffoldingBlock(Properties blockProps) {
6476
super(blockProps);
6577
registerDefaultState(stateDefinition.any()
@@ -129,11 +141,19 @@ public FluidState getFluidState(BlockState pState) {
129141

130142
@Override
131143
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHit) {
132-
if (pPlayer.getItemInHand(pHand).is(FHTags.forgeTag(ForgeRegistries.ITEMS, "rods"))) {
144+
var heldItem = pPlayer.getItemInHand(pHand);
145+
146+
if (heldItem.is(FHTags.forgeTag(ForgeRegistries.ITEMS, "rods"))) {
133147
pLevel.setBlock(pPos, pState.setValue(HANDRAIL, !pState.getValue(HANDRAIL)), 2);
134148
pLevel.playSound((Player)null, pPos, SoundEvents.COPPER_PLACE, SoundSource.BLOCKS, 1.0F, 0.8F + pLevel.random.nextFloat() * 0.4F);
135149
return InteractionResult.SUCCESS;
136150
}
151+
152+
IPlacementHelper helper = PlacementHelpers.get(PLACEMENT_HELPER_ID);
153+
if (helper.matchesItem(heldItem))
154+
return helper.getOffset(pPlayer, pLevel, pState, pPos, pHit)
155+
.placeInWorld(pLevel, (BlockItem) heldItem.getItem(), pPlayer, pHand, pHit);
156+
137157
return super.use(pState, pLevel, pPos, pPlayer, pHand, pHit);
138158
}
139159

@@ -145,7 +165,7 @@ public BlockState updateShape(BlockState pState, Direction pDirection, BlockStat
145165
return calcState(pState, pPos, pLevel);
146166
}
147167

148-
private @NotNull BlockState calcState(BlockState prevState, BlockPos pos, LevelAccessor level) {
168+
public static @NotNull BlockState calcState(BlockState prevState, BlockPos pos, LevelAccessor level) {
149169
var neighbors = getNeighbors(pos);
150170

151171
BlockPos posDown = neighbors.get(Direction.DOWN);
@@ -160,7 +180,7 @@ public BlockState updateShape(BlockState pState, Direction pDirection, BlockStat
160180
boolean crossZ;
161181
boolean surface;
162182

163-
if (upState.getBlock() instanceof FHScaffoldingBlock) {
183+
if (upState.getBlock() instanceof FHScaffoldingBlock || downState.getBlock() instanceof FHScaffoldingBlock) {
164184
crossX = false;
165185
crossZ = false;
166186
} else {
@@ -194,16 +214,83 @@ public BlockState updateShape(BlockState pState, Direction pDirection, BlockStat
194214
.setValue(CROSS_Z, crossZ);
195215
}
196216

197-
public boolean isScaffolding(BlockState state) {
217+
public static boolean isScaffolding(BlockState state) {
198218
var block = state.getBlock();
199219
return block instanceof FHScaffoldingBlock || block instanceof FHScaffoldingStairBlock;
200220
}
201221

202-
private FastEnumMap<Direction, BlockPos> getNeighbors(BlockPos pos) {
222+
public static FastEnumMap<Direction, BlockPos> getNeighbors(BlockPos pos) {
203223
FastEnumMap<Direction, BlockPos> map = new FastEnumMap<>(Direction.values());
204224
for (Direction dir : Direction.values()) {
205225
map.put(dir, pos.relative(dir));
206226
}
207227
return map;
208228
}
229+
230+
public static class ScaffoldingPlacementHelper implements IPlacementHelper {
231+
232+
@Override
233+
public Predicate<ItemStack> getItemPredicate() {
234+
return i -> i.getItem() instanceof BlockItem
235+
&& ((BlockItem) i.getItem()).getBlock() instanceof FHScaffoldingBlock;
236+
}
237+
238+
@Override
239+
public Predicate<BlockState> getStatePredicate() {
240+
return FHScaffoldingBlock::isScaffolding;
241+
}
242+
243+
private boolean canExtendToward(BlockState state, Direction side) {
244+
return getStatePredicate().test(state) && side.getAxis() == Direction.Axis.Y;
245+
}
246+
247+
private int attachedBlocks(Level world, BlockPos pos, Direction direction) {
248+
BlockPos checkPos = pos.relative(direction);
249+
int count = 0;
250+
while (getStatePredicate().test(world.getBlockState(checkPos))) {
251+
count++;
252+
checkPos = checkPos.relative(direction);
253+
}
254+
return count;
255+
}
256+
257+
@Override
258+
public PlacementOffset getOffset(Player player, Level world, BlockState state, BlockPos pos, BlockHitResult ray) {
259+
List<Direction> directions;
260+
261+
if (player != null && ray.getDirection() == Direction.UP) {
262+
Direction facing = player.getDirection();
263+
directions = List.of(facing);
264+
} else {
265+
directions = IPlacementHelper.orderedByDistance(pos, ray.getLocation(),
266+
dir -> canExtendToward(state, dir));
267+
}
268+
269+
int range = AllConfigs.server().equipment.placementAssistRange.get();
270+
if (player != null) {
271+
AttributeInstance reach = player.getAttribute(ForgeMod.BLOCK_REACH.get());
272+
if (reach != null && reach.hasModifier(ExtendoGripItem.singleRangeAttributeModifier))
273+
range += 4;
274+
}
275+
276+
for (Direction dir : directions) {
277+
int poles = attachedBlocks(world, pos, dir);
278+
if (poles >= range)
279+
continue;
280+
281+
BlockPos newPos = pos.relative(dir, poles + 1);
282+
int newY = newPos.getY();
283+
if (newY <= world.getMinBuildHeight() || newY >= world.getMaxBuildHeight())
284+
continue;
285+
BlockState newState = world.getBlockState(newPos);
286+
if (!newState.canBeReplaced())
287+
continue;
288+
289+
return PlacementOffset.success(newPos,
290+
bState -> calcState(bState, newPos, world));
291+
}
292+
293+
return PlacementOffset.fail();
294+
}
295+
}
209296
}

src/main/java/com/teammoeg/frostedheart/content/decoration/FHScaffoldingStairBlock.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
package com.teammoeg.frostedheart.content.decoration;
22

33
import com.simibubi.create.AllShapes;
4+
import com.simibubi.create.foundation.placement.IPlacementHelper;
5+
import com.simibubi.create.foundation.placement.PlacementHelpers;
46
import com.simibubi.create.foundation.utility.VoxelShaper;
57
import com.teammoeg.chorda.block.CBlock;
68
import com.teammoeg.frostedheart.bootstrap.common.FHBlocks;
79
import net.minecraft.core.BlockPos;
810
import net.minecraft.core.Direction;
11+
import net.minecraft.world.InteractionHand;
12+
import net.minecraft.world.InteractionResult;
13+
import net.minecraft.world.entity.player.Player;
14+
import net.minecraft.world.item.BlockItem;
915
import net.minecraft.world.item.context.BlockPlaceContext;
1016
import net.minecraft.world.level.BlockGetter;
17+
import net.minecraft.world.level.Level;
1118
import net.minecraft.world.level.LevelAccessor;
1219
import net.minecraft.world.level.block.Block;
1320
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
@@ -17,6 +24,7 @@
1724
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
1825
import net.minecraft.world.level.material.FluidState;
1926
import net.minecraft.world.level.material.Fluids;
27+
import net.minecraft.world.phys.BlockHitResult;
2028
import net.minecraft.world.phys.shapes.CollisionContext;
2129
import net.minecraft.world.phys.shapes.Shapes;
2230
import net.minecraft.world.phys.shapes.VoxelShape;
@@ -49,6 +57,17 @@ protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockSt
4957
.setValue(HorizontalDirectionalBlock.FACING, pContext.getHorizontalDirection());
5058
}
5159

60+
@Override
61+
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHit) {
62+
var heldItem = pPlayer.getItemInHand(pHand);
63+
IPlacementHelper helper = PlacementHelpers.get(FHScaffoldingBlock.PLACEMENT_HELPER_ID);
64+
if (helper.matchesItem(heldItem))
65+
return helper.getOffset(pPlayer, pLevel, pState, pPos, pHit)
66+
.placeInWorld(pLevel, (BlockItem) heldItem.getItem(), pPlayer, pHand, pHit);
67+
68+
return super.use(pState, pLevel, pPos, pPlayer, pHand, pHit);
69+
}
70+
5271
public FluidState getFluidState(BlockState pState) {
5372
return pState.getValue(BlockStateProperties.WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(pState);
5473
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.teammoeg.frostedheart.content.decoration;
2+
3+
import com.simibubi.create.content.equipment.extendoGrip.ExtendoGripItem;
4+
import com.simibubi.create.foundation.mixin.accessor.UseOnContextAccessor;
5+
import com.simibubi.create.foundation.placement.IPlacementHelper;
6+
import com.simibubi.create.foundation.placement.PlacementHelpers;
7+
import com.simibubi.create.foundation.placement.PlacementOffset;
8+
import com.simibubi.create.infrastructure.config.AllConfigs;
9+
import net.minecraft.core.BlockPos;
10+
import net.minecraft.core.Direction;
11+
import net.minecraft.world.InteractionResult;
12+
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
13+
import net.minecraft.world.entity.player.Player;
14+
import net.minecraft.world.item.BlockItem;
15+
import net.minecraft.world.item.ItemStack;
16+
import net.minecraft.world.item.context.UseOnContext;
17+
import net.minecraft.world.level.Level;
18+
import net.minecraft.world.level.block.Block;
19+
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
20+
import net.minecraft.world.level.block.state.BlockState;
21+
import net.minecraft.world.phys.BlockHitResult;
22+
import net.minecraftforge.common.ForgeMod;
23+
24+
import java.util.function.Predicate;
25+
26+
public class FHScaffoldingStairBlockItem extends BlockItem {
27+
28+
public static final int PLACEMENT_HELPER_ID = PlacementHelpers.register(new PlacementHelper());
29+
30+
public FHScaffoldingStairBlockItem(Block pBlock, Properties pProperties) {
31+
super(pBlock, pProperties);
32+
}
33+
34+
@Override
35+
public InteractionResult useOn(UseOnContext context) {
36+
var player = context.getPlayer();
37+
if (player != null && !player.isCrouching()) {
38+
var heldItem = context.getItemInHand();
39+
var level = context.getLevel();
40+
var pos = context.getClickedPos();
41+
var state = level.getBlockState(pos);
42+
IPlacementHelper helper = PlacementHelpers.get(PLACEMENT_HELPER_ID);
43+
if (helper.matchesItem(heldItem) && helper.matchesState(state)) {
44+
var hit = ((UseOnContextAccessor)context).create$getHitResult();
45+
return helper.getOffset(player, level, state, pos, hit)
46+
.placeInWorld(level, (BlockItem) heldItem.getItem(), player, context.getHand(), hit);
47+
}
48+
}
49+
50+
return super.useOn(context);
51+
}
52+
53+
public static class PlacementHelper implements IPlacementHelper {
54+
55+
@Override
56+
public Predicate<ItemStack> getItemPredicate() {
57+
return i -> i.getItem() instanceof BlockItem
58+
&& ((BlockItem) i.getItem()).getBlock() instanceof FHScaffoldingStairBlock;
59+
}
60+
61+
@Override
62+
public Predicate<BlockState> getStatePredicate() {
63+
return FHScaffoldingBlock::isScaffolding;
64+
}
65+
66+
@Override
67+
public PlacementOffset getOffset(Player player, Level world, BlockState state, BlockPos pos, BlockHitResult ray) {
68+
if (player == null)
69+
return PlacementOffset.fail();
70+
71+
Direction facing = player.getDirection();
72+
BlockPos newPos = pos.relative(facing).above();
73+
if (FHScaffoldingBlock.isScaffolding(world.getBlockState(newPos))) {
74+
int range = AllConfigs.server().equipment.placementAssistRange.get();
75+
AttributeInstance reach = player.getAttribute(ForgeMod.BLOCK_REACH.get());
76+
if (reach != null && reach.hasModifier(ExtendoGripItem.singleRangeAttributeModifier))
77+
range += 4;
78+
if (newPos.getY() <= world.getMaxBuildHeight() && newPos.closerThan(player.getOnPos(), range)) {
79+
return getOffset(player, world, state, newPos, ray);
80+
}
81+
}
82+
int newY = newPos.getY();
83+
if (newY <= world.getMinBuildHeight() || newY >= world.getMaxBuildHeight() || !world.getBlockState(newPos).canBeReplaced())
84+
return PlacementOffset.fail();
85+
86+
return PlacementOffset.success(newPos,
87+
bState -> bState.setValue(HorizontalDirectionalBlock.FACING, facing));
88+
}
89+
}
90+
}

src/main/resources/assets/frostedheart/models/block/scaffolding/stair.json

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,22 @@
1414
"rotation": {"angle": 0, "axis": "y", "origin": [16, 14, 16]},
1515
"faces": {
1616
"north": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all"},
17-
"east": {"uv": [15, 14, 16, 16], "rotation": 90, "texture": "#all", "cullface": "west"},
18-
"south": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all", "cullface": "north"},
19-
"west": {"uv": [15, 8, 16, 10], "rotation": 90, "texture": "#all", "cullface": "east"},
17+
"east": {"uv": [15, 14, 16, 16], "rotation": 90, "texture": "#all", "cullface": "east"},
18+
"south": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all", "cullface": "south"},
19+
"west": {"uv": [15, 8, 16, 10], "rotation": 90, "texture": "#all", "cullface": "west"},
2020
"up": {"uv": [0, 12, 8, 14], "rotation": 180, "texture": "#all"},
2121
"down": {"uv": [0, 12, 8, 14], "rotation": 180, "texture": "#all"}
2222
}
2323
},
2424
{
25-
"from": [0, -2.025, 0],
26-
"to": [16, -0.025, 4],
25+
"from": [-0.025, -2.025, -0.025],
26+
"to": [16.025, 0.025, 4.025],
2727
"rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 16]},
2828
"faces": {
29-
"north": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all", "cullface": "south"},
30-
"east": {"uv": [15, 14, 16, 16], "rotation": 90, "texture": "#all", "cullface": "west"},
29+
"north": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all"},
30+
"east": {"uv": [15, 14, 16, 16], "rotation": 90, "texture": "#all"},
3131
"south": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all"},
32-
"west": {"uv": [15, 14, 16, 16], "rotation": 90, "texture": "#all", "cullface": "east"},
32+
"west": {"uv": [15, 14, 16, 16], "rotation": 90, "texture": "#all"},
3333
"up": {"uv": [0, 12, 8, 14], "rotation": 180, "texture": "#all"},
3434
"down": {"uv": [0, 12, 8, 14], "rotation": 180, "texture": "#all"}
3535
}
@@ -40,9 +40,9 @@
4040
"rotation": {"angle": 0, "axis": "y", "origin": [16, 14, 16]},
4141
"faces": {
4242
"north": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all"},
43-
"east": {"uv": [15, 8.5, 16, 12.5], "rotation": 90, "texture": "#all", "cullface": "west"},
43+
"east": {"uv": [15, 8.5, 16, 12.5], "rotation": 90, "texture": "#all", "cullface": "east"},
4444
"south": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all"},
45-
"west": {"uv": [15, 8.5, 16, 12.5], "rotation": 90, "texture": "#all", "cullface": "east"},
45+
"west": {"uv": [15, 8.5, 16, 12.5], "rotation": 90, "texture": "#all", "cullface": "west"},
4646
"up": {"uv": [0, 8, 8, 12], "rotation": 180, "texture": "#all"},
4747
"down": {"uv": [0, 8, 8, 12], "rotation": 180, "texture": "#all"}
4848
}
@@ -65,14 +65,13 @@
6565
"to": [9.975, 14.47087, 15.8849],
6666
"rotation": {"angle": -45, "axis": "x", "origin": [16.025, 13.99587, 15.9099]},
6767
"faces": {
68-
"north": {"uv": [0, 15, 2, 16], "texture": "#all", "cullface": "south"},
68+
"north": {"uv": [0, 15, 2, 16], "texture": "#all"},
6969
"east": {"uv": [0, 15, 12, 16], "texture": "#all"},
70-
"south": {"uv": [0, 15, 2, 16], "texture": "#all", "cullface": "north"},
70+
"south": {"uv": [0, 15, 2, 16], "texture": "#all", "cullface": "south"},
7171
"west": {"uv": [0, 14, 12, 15], "texture": "#all"},
7272
"up": {"uv": [0, 14, 12, 16], "rotation": 270, "texture": "#all"},
7373
"down": {"uv": [0, 14, 12, 16], "rotation": 270, "texture": "#all"}
7474
}
7575
}
76-
],
77-
"display": {}
76+
]
7877
}

src/main/resources/assets/frostedheart/models/block/scaffolding/surface.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
"elements": [
1212
{
1313
"name": "suface",
14-
"from": [-0.025, 13.975, -0.025],
15-
"to": [16.025, 16.025, 16.025],
14+
"from": [-0.015, 13.985, -0.015],
15+
"to": [16.015, 16.015, 16.015],
1616
"faces": {
1717
"north": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all", "cullface": "north"},
1818
"east": {"uv": [15, 8, 16, 16], "rotation": 90, "texture": "#all", "cullface": "east"},

0 commit comments

Comments
 (0)