Skip to content

Commit 8a04d68

Browse files
committed
feat(mechines): 添加多方块结构控制器系统
- 实现了 BaseControllerBlockEntity 基础类,用于管理多方块结构的成型检测 - 创建了 UniversalMultiblockControllerBlock 抽象类,支持旋转和镜像模式 - 添加了 ExampleUniversalMultiblockControllerBlock 示例实现 - 实现了结构匹配算法,支持三维模式验证 - 注册了新的多方块控制器方块和对应的物品 - 重命名 MananetBlockAssetProvider 为 ManaCableBlockAssetProvider
1 parent ecfa85a commit 8a04d68

File tree

9 files changed

+326
-4
lines changed

9 files changed

+326
-4
lines changed

src/main/java/org/creepebucket/programmable_magic/data/MananetBlockAssetProvider.java renamed to src/main/java/org/creepebucket/programmable_magic/data/ManaCableBlockAssetProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414

1515
import static org.creepebucket.programmable_magic.Programmable_magic.MODID;
1616

17-
public class MananetBlockAssetProvider implements DataProvider {
17+
public class ManaCableBlockAssetProvider implements DataProvider {
1818

1919
private final PackOutput.PathProvider blockstatesProvider;
2020
private final PackOutput.PathProvider blockModelsProvider;
2121
private final PackOutput.PathProvider itemModelsProvider;
2222
private final PackOutput.PathProvider itemsProvider;
2323

24-
public MananetBlockAssetProvider(PackOutput output) {
24+
public ManaCableBlockAssetProvider(PackOutput output) {
2525
this.blockstatesProvider = output.createPathProvider(PackOutput.Target.RESOURCE_PACK, "blockstates");
2626
this.blockModelsProvider = output.createPathProvider(PackOutput.Target.RESOURCE_PACK, "models/block");
2727
this.itemModelsProvider = output.createPathProvider(PackOutput.Target.RESOURCE_PACK, "models/item");

src/main/java/org/creepebucket/programmable_magic/data/ModDataGenerators.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
import net.neoforged.neoforge.data.event.GatherDataEvent;
44
import org.creepebucket.programmable_magic.registries.ModBlockTagProvider;
55
import org.creepebucket.programmable_magic.registries.ModItemTagProvider;
6-
import org.creepebucket.programmable_magic.data.SpellItemModelProvider;
76

87
public class ModDataGenerators {
98
// 使用 Client 子类(与 run config 的 clientData 匹配),保持原先可工作的行为
109
public static void gatherData(GatherDataEvent.Client event) {
1110
event.createProvider(ModBlockTagProvider::new);
1211
event.createProvider(output -> new ModItemTagProvider(output, event.getLookupProvider()));
13-
event.createProvider(MananetBlockAssetProvider::new);
12+
event.createProvider(ManaCableBlockAssetProvider::new);
1413
// 生成法术的模型 + client items(1.21+ 必需)
1514
event.createProvider(SpellItemModelProvider::new);
1615
// 通用物品模型
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.creepebucket.programmable_magic.mechines;
2+
3+
import com.mojang.serialization.MapCodec;
4+
import net.minecraft.core.BlockPos;
5+
import net.minecraft.world.level.block.Blocks;
6+
import net.minecraft.world.level.block.Block;
7+
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
8+
import net.minecraft.world.level.block.entity.BlockEntityType;
9+
import net.minecraft.world.level.block.state.BlockState;
10+
import org.creepebucket.programmable_magic.mechines.api.BaseControllerBlockEntity;
11+
import org.creepebucket.programmable_magic.mechines.api.UniversalMultiblockControllerBlock;
12+
import org.creepebucket.programmable_magic.registries.ModBlockEntities;
13+
14+
import java.util.List;
15+
import java.util.Map;
16+
17+
public class ExampleUniversalMultiblockControllerBlock extends UniversalMultiblockControllerBlock {
18+
19+
public static final MapCodec<ExampleUniversalMultiblockControllerBlock> CODEC = simpleCodec(ExampleUniversalMultiblockControllerBlock::new);
20+
21+
public ExampleUniversalMultiblockControllerBlock(Properties properties) {
22+
super(properties);
23+
}
24+
25+
@Override
26+
protected MapCodec<? extends HorizontalDirectionalBlock> codec() {
27+
return CODEC;
28+
}
29+
30+
@Override
31+
public List<List<String>> pattern() {
32+
return List.of(List.of("# ", "AA", "AA"),
33+
List.of(" ", "A ", "A "));
34+
}
35+
36+
@Override
37+
public Map<Character, Block> map() {
38+
return Map.of('A', Blocks.IRON_BLOCK, ' ', Blocks.AIR);
39+
}
40+
41+
@Override
42+
protected BaseControllerBlockEntity new_controller_block_entity(BlockPos pos, BlockState state) {
43+
return new ExampleUniversalMultiblockControllerBlockEntity(pos, state);
44+
}
45+
46+
@Override
47+
protected BlockEntityType<? extends BaseControllerBlockEntity> controller_block_entity_type() {
48+
return ModBlockEntities.EXAMPLE_UNIVERSAL_MULTIBLOCK_CONTROLLER.get();
49+
}
50+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.creepebucket.programmable_magic.mechines;
2+
3+
import net.minecraft.core.BlockPos;
4+
import net.minecraft.world.level.block.Block;
5+
import net.minecraft.world.level.block.state.BlockState;
6+
import org.creepebucket.programmable_magic.mechines.api.BaseControllerBlockEntity;
7+
import org.creepebucket.programmable_magic.mechines.api.UniversalMultiblockControllerBlock;
8+
import org.creepebucket.programmable_magic.registries.ModBlockEntities;
9+
10+
import java.util.List;
11+
import java.util.Map;
12+
13+
public class ExampleUniversalMultiblockControllerBlockEntity extends BaseControllerBlockEntity {
14+
15+
public ExampleUniversalMultiblockControllerBlockEntity(BlockPos pos, BlockState state) {
16+
super(ModBlockEntities.EXAMPLE_UNIVERSAL_MULTIBLOCK_CONTROLLER.get(), pos, state, pattern(state), map(state));
17+
}
18+
19+
private static List<List<String>> pattern(BlockState state) {
20+
return ((UniversalMultiblockControllerBlock) state.getBlock()).pattern();
21+
}
22+
23+
private static Map<Character, Block> map(BlockState state) {
24+
return ((UniversalMultiblockControllerBlock) state.getBlock()).map();
25+
}
26+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package org.creepebucket.programmable_magic.mechines.api;
2+
3+
import net.minecraft.core.BlockPos;
4+
import net.minecraft.world.level.Level;
5+
import net.minecraft.world.level.block.Block;
6+
import net.minecraft.world.level.block.entity.BlockEntity;
7+
import net.minecraft.world.level.block.entity.BlockEntityType;
8+
import net.minecraft.world.level.block.state.BlockState;
9+
import java.util.List;
10+
import java.util.Map;
11+
12+
public class BaseControllerBlockEntity extends BlockEntity {
13+
/*
14+
* pattern: 描述一个多方块结构.
15+
* 格式为:
16+
*
17+
* List.of(
18+
* List.of("AAAABBBB", "CCCCDDDD"),
19+
* List.of("EEEE#FFF", "GGGGGGGG"),
20+
* )
21+
*
22+
* 约定如下: 称外层列表为i, 内层为j, 则:
23+
* i[x] 代表z坐标, j[x]表示y坐标, String[x]表示x坐标
24+
* String内的每个字符即表示一个方块, 且必须有(且仅有)一个"#"表示控制器位置.
25+
*/
26+
public List<List<String>> pattern;
27+
/*
28+
* map: 决定pattern内的方块如何被映射为方块.
29+
*/
30+
public Map<Character, Block> map;
31+
short nextStructureCheck = 5; // 下一次结构完整性检查还剩 (tick)
32+
boolean formed; // 是否成型
33+
34+
public BaseControllerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState blockState, List<List<String>> pattern, Map<Character, Block> map) {
35+
super(type, pos, blockState);
36+
this.pattern = pattern;
37+
this.map = map;
38+
}
39+
40+
public static void tick(Level level, BlockPos pos, BlockState state, BaseControllerBlockEntity be) {
41+
// 检查结构完整性
42+
if (--be.nextStructureCheck == 0) {
43+
be.nextStructureCheck = (short) Math.floor(60 + Math.random() * 20); // 在3-4秒随机, 均摊负载
44+
be.checkStructure();
45+
}
46+
47+
if (!be.formed) return;
48+
49+
System.out.println("我成型了");
50+
// 主要逻辑
51+
}
52+
53+
private void checkStructure() {
54+
Level level = this.getLevel();
55+
BlockPos controller_pos = this.getBlockPos();
56+
57+
BlockState controller_state = level.getBlockState(controller_pos);
58+
if (controller_state.getBlock() instanceof UniversalMultiblockControllerBlock controller_block) {
59+
Map<Character, Block> map = controller_block.map();
60+
List<List<String>> pattern = controller_block.rotated_pattern(controller_state);
61+
if (matches(level, controller_pos, pattern, map)) {
62+
this.formed = true;
63+
return;
64+
}
65+
this.formed = matches(level, controller_pos, controller_block.rotated_mirrored_pattern(controller_state), map);
66+
return;
67+
}
68+
69+
this.formed = matches(level, controller_pos, this.pattern, this.map);
70+
}
71+
72+
private static boolean matches(Level level, BlockPos controller_pos, List<List<String>> pattern, Map<Character, Block> map) {
73+
int controller_z = 0;
74+
int controller_y = 0;
75+
int controller_x = 0;
76+
for (int z = 0; z < pattern.size(); z++) {
77+
List<String> layer = pattern.get(z);
78+
for (int y = 0; y < layer.size(); y++) {
79+
String row = layer.get(y);
80+
for (int x = 0; x < row.length(); x++) {
81+
if (row.charAt(x) != '#') continue;
82+
controller_z = z;
83+
controller_y = y;
84+
controller_x = x;
85+
}
86+
}
87+
}
88+
89+
boolean formed = true;
90+
for (int z = 0; z < pattern.size(); z++) {
91+
List<String> layer = pattern.get(z);
92+
for (int y = 0; y < layer.size(); y++) {
93+
String row = layer.get(y);
94+
for (int x = 0; x < row.length(); x++) {
95+
char ch = row.charAt(x);
96+
if (ch == '#') continue;
97+
98+
Block expected = map.get(ch);
99+
BlockPos target_pos = controller_pos.offset(x - controller_x, y - controller_y, z - controller_z);
100+
if (!level.getBlockState(target_pos).is(expected)) formed = false;
101+
}
102+
}
103+
}
104+
return formed;
105+
}
106+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package org.creepebucket.programmable_magic.mechines.api;
2+
3+
import net.minecraft.core.BlockPos;
4+
import net.minecraft.core.Direction;
5+
import net.minecraft.world.level.Level;
6+
import net.minecraft.world.level.block.Block;
7+
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
8+
import net.minecraft.world.level.block.EntityBlock;
9+
import net.minecraft.world.level.block.entity.BlockEntity;
10+
import net.minecraft.world.level.block.entity.BlockEntityTicker;
11+
import net.minecraft.world.level.block.entity.BlockEntityType;
12+
import net.minecraft.world.level.block.state.BlockState;
13+
import net.minecraft.world.level.block.state.StateDefinition;
14+
import net.minecraft.world.item.context.BlockPlaceContext;
15+
16+
import java.util.List;
17+
import java.util.Map;
18+
import javax.annotation.Nullable;
19+
20+
public abstract class UniversalMultiblockControllerBlock extends HorizontalDirectionalBlock implements EntityBlock {
21+
22+
public UniversalMultiblockControllerBlock(Properties p_49795_) {
23+
super(p_49795_);
24+
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH));
25+
}
26+
27+
public abstract List<List<String>> pattern();
28+
public abstract Map<Character, Block> map();
29+
30+
public final List<List<String>> rotated_pattern(BlockState state) {
31+
int steps = switch (state.getValue(FACING)) {
32+
case NORTH -> 0;
33+
case EAST -> 1;
34+
case SOUTH -> 2;
35+
case WEST -> 3;
36+
default -> 0;
37+
};
38+
39+
List<List<String>> rotated = pattern();
40+
for (int i = 0; i < steps; i++) rotated = rotate_pattern_clockwise(rotated);
41+
return rotated;
42+
}
43+
44+
public final List<List<String>> rotated_mirrored_pattern(BlockState state) {
45+
List<List<String>> rotated = rotated_pattern(state);
46+
return switch (state.getValue(FACING)) {
47+
case NORTH, SOUTH -> mirror_z(rotated);
48+
case EAST, WEST -> mirror_x(rotated);
49+
default -> rotated;
50+
};
51+
}
52+
53+
@Override
54+
public BlockState getStateForPlacement(BlockPlaceContext context) {
55+
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
56+
}
57+
58+
@Override
59+
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
60+
builder.add(FACING);
61+
}
62+
63+
private static List<List<String>> rotate_pattern_clockwise(List<List<String>> pattern) {
64+
int x_len = pattern.size();
65+
int y_len = pattern.getFirst().size();
66+
int z_len = pattern.getFirst().getFirst().length();
67+
68+
java.util.ArrayList<List<String>> out = new java.util.ArrayList<>(z_len);
69+
for (int new_x = 0; new_x < z_len; new_x++) {
70+
java.util.ArrayList<String> layer = new java.util.ArrayList<>(y_len);
71+
for (int y = 0; y < y_len; y++) {
72+
StringBuilder sb = new StringBuilder(x_len);
73+
for (int new_z = 0; new_z < x_len; new_z++) {
74+
int old_x = new_z;
75+
int old_z = z_len - 1 - new_x;
76+
sb.append(pattern.get(old_x).get(y).charAt(old_z));
77+
}
78+
layer.add(sb.toString());
79+
}
80+
out.add(layer);
81+
}
82+
return out;
83+
}
84+
85+
private static List<List<String>> mirror_x(List<List<String>> pattern) {
86+
java.util.ArrayList<List<String>> out = new java.util.ArrayList<>(pattern.size());
87+
for (int x = pattern.size() - 1; x >= 0; x--) out.add(pattern.get(x));
88+
return out;
89+
}
90+
91+
private static List<List<String>> mirror_z(List<List<String>> pattern) {
92+
int x_len = pattern.size();
93+
int y_len = pattern.getFirst().size();
94+
95+
java.util.ArrayList<List<String>> out = new java.util.ArrayList<>(x_len);
96+
for (int x = 0; x < x_len; x++) {
97+
java.util.ArrayList<String> layer = new java.util.ArrayList<>(y_len);
98+
for (int y = 0; y < y_len; y++) layer.add(new StringBuilder(pattern.get(x).get(y)).reverse().toString());
99+
out.add(layer);
100+
}
101+
return out;
102+
}
103+
104+
protected abstract BaseControllerBlockEntity new_controller_block_entity(BlockPos pos, BlockState state);
105+
protected abstract BlockEntityType<? extends BaseControllerBlockEntity> controller_block_entity_type();
106+
107+
@Nullable
108+
@Override
109+
public final BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
110+
return new_controller_block_entity(pos, state);
111+
}
112+
113+
@Nullable
114+
@Override
115+
public final <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
116+
if (level.isClientSide) return null;
117+
return type == controller_block_entity_type()
118+
? (lvl, pos, st, be) -> BaseControllerBlockEntity.tick(lvl, pos, st, (BaseControllerBlockEntity) be)
119+
: null;
120+
}
121+
}

src/main/java/org/creepebucket/programmable_magic/registries/ModBlockEntities.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
import net.neoforged.bus.api.IEventBus;
66
import java.util.function.Supplier;
77
import net.neoforged.neoforge.registries.DeferredRegister;
8+
import org.creepebucket.programmable_magic.mechines.ExampleUniversalMultiblockControllerBlockEntity;
89

910
import static org.creepebucket.programmable_magic.Programmable_magic.MODID;
1011

1112
public class ModBlockEntities {
1213

1314
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITIES = DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, MODID);
1415

16+
public static final Supplier<BlockEntityType<ExampleUniversalMultiblockControllerBlockEntity>> EXAMPLE_UNIVERSAL_MULTIBLOCK_CONTROLLER =
17+
BLOCK_ENTITIES.register("example_universal_multiblock_controller",
18+
() -> new BlockEntityType<>(ExampleUniversalMultiblockControllerBlockEntity::new, false, ModBlocks.EXAMPLE_UNIVERSAL_MULTIBLOCK_CONTROLLER.get()));
19+
1520
public static void register(IEventBus bus) {BLOCK_ENTITIES.register(bus);}
1621
}

src/main/java/org/creepebucket/programmable_magic/registries/ModBlocks.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@
77
import net.neoforged.bus.api.IEventBus;
88
import net.neoforged.neoforge.registries.DeferredBlock;
99
import net.neoforged.neoforge.registries.DeferredRegister;
10+
import org.creepebucket.programmable_magic.mechines.ExampleUniversalMultiblockControllerBlock;
1011

1112
import static org.creepebucket.programmable_magic.Programmable_magic.MODID;
1213

1314
public class ModBlocks {
1415

1516
public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MODID);
1617

18+
public static final DeferredBlock<ExampleUniversalMultiblockControllerBlock> EXAMPLE_UNIVERSAL_MULTIBLOCK_CONTROLLER = BLOCKS.register(
19+
"example_universal_multiblock_controller",
20+
registryName -> new ExampleUniversalMultiblockControllerBlock(BlockBehaviour.Properties.of()
21+
.mapColor(MapColor.STONE)
22+
.strength(2.0f)
23+
.setId(ResourceKey.create(Registries.BLOCK, registryName)))
24+
);
25+
1726
public static void register(IEventBus bus) {BLOCKS.register(bus);}
1827
}

src/main/java/org/creepebucket/programmable_magic/registries/ModItems.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ public class ModItems {
2121

2222
public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID);
2323

24+
public static final DeferredItem<BlockItem> EXAMPLE_UNIVERSAL_MULTIBLOCK_CONTROLLER = ITEMS.register(
25+
"example_universal_multiblock_controller",
26+
registryName -> new BlockItem(ModBlocks.EXAMPLE_UNIVERSAL_MULTIBLOCK_CONTROLLER.get(), new Item.Properties()
27+
.setId(ResourceKey.create(Registries.ITEM, registryName)))
28+
);
29+
2430
public static final DeferredItem<SmallManaCell> SMALL_MANA_CELL_DEFERRED_ITEM = ITEMS.register(
2531
"small_mana_cell", registryName -> new SmallManaCell(new Item.Properties()
2632
.setId(ResourceKey.create(Registries.ITEM, registryName)))

0 commit comments

Comments
 (0)