diff --git a/src/generated/resources/assets/anvilcraft/blockstates/pump.json b/src/generated/resources/assets/anvilcraft/blockstates/pump.json new file mode 100644 index 0000000000..1f8fa498b3 --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/blockstates/pump.json @@ -0,0 +1,216 @@ +{ + "variants": { + "orientation=down_east,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "x": -90, + "y": -90 + }, + "orientation=down_east,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "x": -90, + "y": -90 + }, + "orientation=down_east,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "x": -90, + "y": -90 + }, + "orientation=down_east,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "x": -90, + "y": -90 + }, + "orientation=down_north,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "x": -90, + "y": 180 + }, + "orientation=down_north,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "x": -90, + "y": 180 + }, + "orientation=down_north,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "x": -90, + "y": 180 + }, + "orientation=down_north,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "x": -90, + "y": 180 + }, + "orientation=down_south,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "x": -90 + }, + "orientation=down_south,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "x": -90 + }, + "orientation=down_south,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "x": -90 + }, + "orientation=down_south,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "x": -90 + }, + "orientation=down_west,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "x": -90, + "y": 90 + }, + "orientation=down_west,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "x": -90, + "y": 90 + }, + "orientation=down_west,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "x": -90, + "y": 90 + }, + "orientation=down_west,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "x": -90, + "y": 90 + }, + "orientation=east_up,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "y": -90 + }, + "orientation=east_up,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "y": -90 + }, + "orientation=east_up,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "y": -90 + }, + "orientation=east_up,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "y": -90 + }, + "orientation=north_up,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "y": 180 + }, + "orientation=north_up,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "y": 180 + }, + "orientation=north_up,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "y": 180 + }, + "orientation=north_up,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "y": 180 + }, + "orientation=south_up,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base" + }, + "orientation=south_up,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off" + }, + "orientation=south_up,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload" + }, + "orientation=south_up,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload" + }, + "orientation=up_east,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "x": 90, + "y": -90 + }, + "orientation=up_east,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "x": 90, + "y": -90 + }, + "orientation=up_east,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "x": 90, + "y": -90 + }, + "orientation=up_east,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "x": 90, + "y": -90 + }, + "orientation=up_north,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "x": 90, + "y": 180 + }, + "orientation=up_north,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "x": 90, + "y": 180 + }, + "orientation=up_north,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "x": 90, + "y": 180 + }, + "orientation=up_north,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "x": 90, + "y": 180 + }, + "orientation=up_south,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "x": 90 + }, + "orientation=up_south,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "x": 90 + }, + "orientation=up_south,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "x": 90 + }, + "orientation=up_south,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "x": 90 + }, + "orientation=up_west,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "x": 90, + "y": 90 + }, + "orientation=up_west,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "x": 90, + "y": 90 + }, + "orientation=up_west,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "x": 90, + "y": 90 + }, + "orientation=up_west,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "x": 90, + "y": 90 + }, + "orientation=west_up,overload=false,powered=false": { + "model": "anvilcraft:block/pump_base", + "y": 90 + }, + "orientation=west_up,overload=false,powered=true": { + "model": "anvilcraft:block/pump_off", + "y": 90 + }, + "orientation=west_up,overload=true,powered=false": { + "model": "anvilcraft:block/pump_overload", + "y": 90 + }, + "orientation=west_up,overload=true,powered=true": { + "model": "anvilcraft:block/pump_overload", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index a77762ba75..72ccaf05d1 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -478,6 +478,7 @@ "block.anvilcraft.powered_sliding_rail": "ꞁᴉɐᴚ ᵷuᴉpᴉꞁS pǝɹǝʍoԀ", "block.anvilcraft.propel_piston": "uoʇsᴉԀ ꞁǝdoɹԀ", "block.anvilcraft.pulse_generator": "ɹoʇɐɹǝuǝ⅁ ǝsꞁnԀ", + "block.anvilcraft.pump": "dɯnԀ", "block.anvilcraft.purple_cement": "ʇuǝɯǝƆ ǝꞁdɹnԀ", "block.anvilcraft.purple_cement_cauldron": "uoɹpꞁnɐƆ ʇuǝɯǝƆ ǝꞁdɹnԀ", "block.anvilcraft.quartz_sand": "puɐS zʇɹɐnꝹ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index ad40c7356e..662897170b 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -478,6 +478,7 @@ "block.anvilcraft.powered_sliding_rail": "Powered Sliding Rail", "block.anvilcraft.propel_piston": "Propel Piston", "block.anvilcraft.pulse_generator": "Pulse Generator", + "block.anvilcraft.pump": "Pump", "block.anvilcraft.purple_cement": "Purple Cement", "block.anvilcraft.purple_cement_cauldron": "Purple Cement Cauldron", "block.anvilcraft.quartz_sand": "Quartz Sand", diff --git a/src/generated/resources/assets/anvilcraft/models/item/pump.json b/src/generated/resources/assets/anvilcraft/models/item/pump.json new file mode 100644 index 0000000000..b9f0eea474 --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/models/item/pump.json @@ -0,0 +1,3 @@ +{ + "parent": "anvilcraft:block/pump" +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/misc/pump.json b/src/generated/resources/data/anvilcraft/advancement/recipes/misc/pump.json new file mode 100644 index 0000000000..2f680f6f07 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/misc/pump.json @@ -0,0 +1,43 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_pipe": { + "conditions": { + "items": [ + { + "items": "anvilcraft:pipe" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_piston": { + "conditions": { + "items": [ + { + "items": "minecraft:piston" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:pump" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_piston", + "has_pipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:pump" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/loot_table/blocks/pump.json b/src/generated/resources/data/anvilcraft/loot_table/blocks/pump.json new file mode 100644 index 0000000000..667d302966 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/loot_table/blocks/pump.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "anvilcraft:pump" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "anvilcraft:blocks/pump" +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/pump.json b/src/generated/resources/data/anvilcraft/recipe/pump.json new file mode 100644 index 0000000000..244b6fe268 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/pump.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "anvilcraft:pump", + "key": { + "C": { + "item": "anvilcraft:pipe" + }, + "P": { + "item": "minecraft:piston" + } + }, + "pattern": [ + "PCP", + "P P", + " " + ], + "result": { + "count": 2, + "id": "anvilcraft:pump" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/tags/block/collision_immune.json b/src/generated/resources/data/anvilcraft/tags/block/collision_immune.json index 81e753a7d3..99fee903e9 100644 --- a/src/generated/resources/data/anvilcraft/tags/block/collision_immune.json +++ b/src/generated/resources/data/anvilcraft/tags/block/collision_immune.json @@ -1,6 +1,7 @@ { "values": [ "anvilcraft:transcendence_anvil", + "anvilcraft:infinite_collector", "anvilcraft:celestial_forging_anvil", "anvilcraft:celestial_forging_anvil_portal", "anvilcraft:transcendium_block", @@ -10,7 +11,6 @@ "anvilcraft:confined_energy_anvilon", "anvilcraft:confined_neutronium_ingot", "anvilcraft:confinement_chamber", - "anvilcraft:singularity_crystal", - "anvilcraft:infinite_collector" + "anvilcraft:singularity_crystal" ] } \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json index 68716ffe95..3ee28002e0 100644 --- a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json +++ b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json @@ -317,6 +317,7 @@ "anvilcraft:creative_crate", "anvilcraft:pipe_straight", "anvilcraft:pipe_corner", - "anvilcraft:pipe_node" + "anvilcraft:pipe_node", + "anvilcraft:pump" ] } \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/AbstractPipeBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/AbstractPipeBlockEntity.java index b41d1c6a42..75bac5b6ea 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/AbstractPipeBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/AbstractPipeBlockEntity.java @@ -4,6 +4,7 @@ import dev.dubhe.anvilcraft.block.fluid.PipeCornerBlock; import dev.dubhe.anvilcraft.block.fluid.PipeNodeBlock; import dev.dubhe.anvilcraft.block.fluid.PipeStraightBlock; +import dev.dubhe.anvilcraft.block.fluid.PumpBlock; import lombok.Getter; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -36,48 +37,16 @@ * 限制流速(每格高度差 50 mB/tick)。 * *

等效高度(Effective Height)

- * 每个管道 BlockEntity 持有一个 {@link #heightBonus} 字段, * 代表该段管道的等效高度偏移(泵可增大此值实现扬程)。 * 流体传输和 PipeEnd 排序均以 {@code pos.getY() + heightBonus} 作为等效高度, * 而非真实 Y 坐标。 */ @Getter public abstract class AbstractPipeBlockEntity extends BlockEntity { - - /** - * 本段管道的等效高度偏移。泵等设备可设置为正值以实现扬程。 - * 最终等效高度 = {@code getBlockPos().getY() + heightBonus}。 - */ - protected int heightBonus; - protected AbstractPipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { super(type, pos, blockState); } - /** - * 本段管道的等效高度 - * - * @return 本段管道的等效高度(真实Y + 偏移) - */ - public int getEffectiveHeight() { - return this.getBlockPos().getY() + this.heightBonus; - } - - /** - * 设置本段管道的等效高度偏移。 - * - * @param bonus 偏移量(正值提高等效高度,实现扬程) - */ - public void setHeightBonus(int bonus) { - if (this.heightBonus != bonus) { - this.heightBonus = bonus; - this.setChanged(); - if (this.level != null && !this.level.isClientSide()) { - this.sendUpdate(); - } - } - } - /** * 触发客户端渲染更新 */ @@ -115,20 +84,23 @@ protected void sendNeighbourUpdate() { } BlockState blockState = level.getBlockState(blockPos); - // 累加当前位置管道的高度偏移 - int bonus = 0; - if (level.getBlockEntity(blockPos) instanceof AbstractPipeBlockEntity pipeBe) { - bonus = pipeBe.getHeightBonus(); - } - if (blockState.getBlock() instanceof PipeNodeBlock) { - return new PipeEnd(blockPos.relative(direction.getOpposite()), direction, accumulatedHeight + bonus + blockPos.getY()); + return new PipeEnd(blockPos.relative(direction.getOpposite()), direction, accumulatedHeight); } if (blockState.getBlock() instanceof PipeStraightBlock) { - return getPipeStraightEnd(level, blockPos, blockState, direction, accumulatedHeight + bonus); + return getPipeStraightEnd(level, blockPos, blockState, direction, accumulatedHeight); } if (blockState.getBlock() instanceof PipeCornerBlock) { - return getPipeCornerEnd(level, blockPos, blockState, direction, accumulatedHeight + bonus); + return getPipeCornerEnd(level, blockPos, blockState, direction, accumulatedHeight); + } + if (blockState.getBlock() instanceof PumpBlock) { + Direction pumpOutputDir = blockState.getValue(PumpBlock.ORIENTATION).getDirection(); + if (direction == pumpOutputDir && level.getBlockEntity(blockPos) instanceof PumpBlockEntity pumpBe && pumpBe.canPump()) { + // 泵可工作且方向匹配 → 等效距离 +10 并继续追踪 + return getPumpPipeEnd(level, blockPos, direction, accumulatedHeight); + } + // 方向不匹配或泵不能工作 → 清空等效距离并返回 + return null; } return null; } @@ -169,7 +141,12 @@ protected void sendNeighbourUpdate() { } Direction targetDir = direction.getOpposite(); if (!hasNext) { - return new PipeEnd(blockPos, targetDir, accumulatedHeight + blockPos.getY()); + // 检查端头指向的方块是否是泵,若是则继续追踪 + BlockPos neighborPos = blockPos.relative(targetDir); + if (level.getBlockState(neighborPos).getBlock() instanceof PumpBlock) { + return getPipeEnd(level, neighborPos, direction, accumulatedHeight); + } + return new PipeEnd(blockPos, targetDir, accumulatedHeight); } return getPipeEnd(level, blockPos.relative(targetDir), direction, accumulatedHeight); } @@ -205,41 +182,82 @@ protected void sendNeighbourUpdate() { targetDir = startDir; } if (!hasNext) { - return new PipeEnd(blockPos, targetDir, accumulatedHeight + blockPos.getY()); + // 检查端头指向的方块是否是泵,若是则继续追踪 + BlockPos neighborPos = blockPos.relative(targetDir); + if (level.getBlockState(neighborPos).getBlock() instanceof PumpBlock) { + return getPipeEnd(level, neighborPos, targetDir.getOpposite(), accumulatedHeight); + } + return new PipeEnd(blockPos, targetDir, accumulatedHeight); } return getPipeEnd(level, blockPos.relative(targetDir), targetDir.getOpposite(), accumulatedHeight); } + /** + * 从泵的输出端继续追踪 PipeEnd。 + * + * + * @param level 世界 + * @param pumpPos 泵的位置 + * @param direction 追踪方向(泵的输出方向) + * @param accumulatedHeight 已累积的等效高度(含泵的 heightBonus) + * @return PipeEnd,不可达时返回 null + */ + private static @Nullable PipeEnd getPumpPipeEnd(Level level, BlockPos pumpPos, Direction direction, int accumulatedHeight) { + BlockPos nextPos = pumpPos.relative(direction.getOpposite()); + if (!level.isLoaded(nextPos)) return null; + + BlockState nextState = level.getBlockState(nextPos); + + // 若相邻是管道或泵,继续管道追踪 + if ( + nextState.getBlock() instanceof PipeNodeBlock + || nextState.getBlock() instanceof PipeStraightBlock + || nextState.getBlock() instanceof PipeCornerBlock + || nextState.getBlock() instanceof PumpBlock + ) { + return getPipeEnd(level, nextPos, direction, accumulatedHeight + PumpBlockEntity.PUMP_HEADLIFT); + } + + // 若相邻是流体处理器,泵输出端直接对其排液 + if (PipeBlock.isFluidHandler(level, nextPos)) { + return new PipeEnd(pumpPos, direction.getOpposite(), accumulatedHeight + PumpBlockEntity.PUMP_HEADLIFT); + } + + return null; + } + /** * 流体传输(带高度差检查):使用等效高度替代真实 Y 坐标。 * 仅在源等效高度高于目标等效高度时执行传输。 + * + *

高度差基于移位后的实际连接位置(sourcePos/targetPos), + * 而非管道自身位置,以正确处理同方块两端端头的情况。 */ public static void moveFluidWithHeightCheck( Level level, BlockPos sourceCurPos, Direction sourceCurDirection, BlockPos targetCurPos, - Direction targetCurDirection + Direction targetCurDirection, + int effectiveHeight ) { - // 计算源的等效高度 - int sourceEffectiveY = sourceCurPos.getY(); - int targetEffectiveY = targetCurPos.getY(); - if (level.getBlockEntity(sourceCurPos) instanceof AbstractPipeBlockEntity sourceBe) { - sourceEffectiveY = sourceBe.getEffectiveHeight(); - } - if (level.getBlockEntity(targetCurPos) instanceof AbstractPipeBlockEntity targetBe) { - targetEffectiveY = targetBe.getEffectiveHeight(); - } - - if (sourceEffectiveY <= targetEffectiveY) { - return; - } - BlockPos sourcePos = sourceCurPos.relative(sourceCurDirection); BlockPos targetPos = targetCurPos.relative(targetCurDirection); + + // 用移位后的实际连接位置计算基础高度 + int sourceEffectiveY = sourcePos.getY(); + int targetEffectiveY = targetPos.getY(); + + targetEffectiveY -= effectiveHeight; + + if (sourceEffectiveY <= targetEffectiveY) return; + Direction sourceDirection = sourceCurDirection.getOpposite(); Direction targetDirection = targetCurDirection.getOpposite(); - // 使用等效高度差计算流速 moveFluid(level, sourcePos, sourceDirection, targetPos, targetDirection, sourceEffectiveY - targetEffectiveY); } diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PipeBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PipeBlockEntity.java index 2bafe5b801..a8f66637d0 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PipeBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PipeBlockEntity.java @@ -3,6 +3,7 @@ import dev.dubhe.anvilcraft.block.fluid.PipeBlock; import dev.dubhe.anvilcraft.block.fluid.PipeCornerBlock; import dev.dubhe.anvilcraft.block.fluid.PipeStraightBlock; +import dev.dubhe.anvilcraft.block.fluid.PumpBlock; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.HolderLookup; @@ -61,24 +62,16 @@ public static int getEndCount(BlockState blockState) { @Override protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.saveAdditional(tag, registries); - if (this.heightBonus != 0) { - tag.putInt("HeightBonus", this.heightBonus); - } } @Override public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.loadAdditional(tag, registries); - this.heightBonus = tag.getInt("HeightBonus"); } @Override public CompoundTag getUpdateTag(HolderLookup.Provider registries) { - CompoundTag tag = super.getUpdateTag(registries); - if (this.heightBonus != 0) { - tag.putInt("HeightBonus", this.heightBonus); - } - return tag; + return super.getUpdateTag(registries); } @Override @@ -91,8 +84,9 @@ public Packet getUpdatePacket() { /** * Per-tick 排液逻辑。 * - *

仅当有端头时才执行。两端端头且非垂直/非垂直弯管时跳过(避免水平管误排)。 - * 单端端头时沿无端头端追踪 PipeEnd 并向其排液。 + *

仅当有端头时才执行。两端端头时按管轴方向排液, + * 端头指向泵时自动透传追踪(不提前返回)。 + * 单端端头时沿无端头方向追踪 PipeEnd 并向其排液。 */ public static void tick(Level level, BlockPos pos, BlockState state) { int endCount = getEndCount(state); @@ -102,34 +96,24 @@ public static void tick(Level level, BlockPos pos, BlockState state) { boolean isStraight = state.getBlock() instanceof PipeStraightBlock; - // 两端端头 + 水平直管 → 跳过 - if (endCount == 2 && isStraight && !Direction.Axis.Y.equals(state.getValue(PipeStraightBlock.AXIS))) { - return; - } - - // 两端端头 + 水平弯管(不涉及 Y 轴)→ 跳过 - if ( - endCount == 2 && !isStraight - && !state.getValue(PipeCornerBlock.CORNER_ENDED).getFirstDirection().equals(Direction.DOWN) - && !state.getValue(PipeCornerBlock.CORNER_ENDED).getFirstDirection().equals(Direction.UP) - ) { - return; - } - if (endCount == 2) { - // 两端端头 → 同柱/同管排液 + // 两端端头 → 按管轴方向排液,检查端头是否指向泵 if (isStraight) { - // 垂直直管:上端 → 下端 - AbstractPipeBlockEntity.moveFluidWithHeightCheck(level, pos, Direction.UP, pos, Direction.DOWN); + Direction.Axis axis = state.getValue(PipeStraightBlock.AXIS); + Direction posDir = PipeBlock.getDirectionFromAxis(axis, Direction.AxisDirection.POSITIVE); + Direction negDir = PipeBlock.getDirectionFromAxis(axis, Direction.AxisDirection.NEGATIVE); + + // 检查端头指向的方块是否是泵,若是则透传追踪 + tickEndCount2(level, pos, posDir, negDir); + tickEndCount2(level, pos, negDir, posDir); } else { - // 垂直弯管:垂直端 → 水平端(或反向) - if (state.getValue(PipeCornerBlock.CORNER_ENDED).getFirstDirection().equals(Direction.DOWN)) { - PipeBlock.CornerEnded cornerEnded = state.getValue(PipeCornerBlock.CORNER_ENDED); - AbstractPipeBlockEntity.moveFluidWithHeightCheck(level, pos, cornerEnded.getSecondDirection(), pos, Direction.DOWN); - } else { - PipeBlock.CornerEnded cornerEnded = state.getValue(PipeCornerBlock.CORNER_ENDED); - AbstractPipeBlockEntity.moveFluidWithHeightCheck(level, pos, Direction.UP, pos, cornerEnded.getSecondDirection()); - } + // 弯管两端端头 + PipeBlock.CornerEnded cornerEnded = state.getValue(PipeCornerBlock.CORNER_ENDED); + Direction firstDir = cornerEnded.getFirstDirection(); + Direction secondDir = cornerEnded.getSecondDirection(); + + tickEndCount2(level, pos, firstDir, secondDir); + tickEndCount2(level, pos, secondDir, firstDir); } return; } @@ -156,6 +140,43 @@ public static void tick(Level level, BlockPos pos, BlockState state) { if (pipeEnd == null) { return; } - AbstractPipeBlockEntity.moveFluidWithHeightCheck(level, pos, sourceDirection, pipeEnd.pos(), pipeEnd.direction()); + AbstractPipeBlockEntity.moveFluidWithHeightCheck( + level, + pos, + sourceDirection, + pipeEnd.pos(), + pipeEnd.direction(), + pipeEnd.effectiveHeight() + ); + } + + private static void tickEndCount2(Level level, BlockPos pos, Direction posDir, Direction negDir) { + BlockPos targetCurPos = pos; + Direction targetCurDir = negDir; + int effectiveHeight = 0; + + BlockPos sourceNeighbor = pos.relative(posDir); + if (level.getBlockState(sourceNeighbor).getBlock() instanceof PumpBlock) { + return; + } + + BlockPos targetNeighbor = pos.relative(negDir); + if (level.getBlockState(targetNeighbor).getBlock() instanceof PumpBlock) { + PipeEnd pumpEnd = getPipeEnd(level, targetNeighbor, negDir.getOpposite()); + if (pumpEnd != null) { + targetCurPos = pumpEnd.pos(); + targetCurDir = pumpEnd.direction(); + effectiveHeight = pumpEnd.effectiveHeight(); + } + } + + AbstractPipeBlockEntity.moveFluidWithHeightCheck( + level, + pos, + posDir, + targetCurPos, + targetCurDir, + effectiveHeight + ); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PipeNodeBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PipeNodeBlockEntity.java index 0116ec23b2..b44c41f6e9 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PipeNodeBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PipeNodeBlockEntity.java @@ -3,6 +3,7 @@ import dev.dubhe.anvilcraft.api.fluid.IFluidHandlerHolder; import dev.dubhe.anvilcraft.block.fluid.PipeBlock; import dev.dubhe.anvilcraft.block.fluid.PipeNodeBlock; +import dev.dubhe.anvilcraft.block.fluid.PumpBlock; import lombok.Getter; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -80,16 +81,12 @@ protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) if (!tankNbt.isEmpty()) { tag.put("Fluid", tankNbt); } - if (this.heightBonus != 0) { - tag.putInt("HeightBonus", this.heightBonus); - } } @Override public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.loadAdditional(tag, registries); this.fluidHandler.readFromNBT(registries, tag.getCompound("Fluid")); - this.heightBonus = tag.getInt("HeightBonus"); } @Override @@ -99,9 +96,6 @@ public CompoundTag getUpdateTag(HolderLookup.Provider registries) { if (!tankNbt.isEmpty()) { tag.put("Fluid", tankNbt); } - if (this.heightBonus != 0) { - tag.putInt("HeightBonus", this.heightBonus); - } return tag; } @@ -128,15 +122,57 @@ public static void tick(Level level, BlockPos pos, BlockState state) { EnumProperty property = PipeBlock.getPropertyForDirection(direction); PipeBlock.NodePipe value = state.getValue(property); - // END + UP:向上方排液 + // END + UP:向上方排液(若端头指向泵则透传追踪) if (value.equals(PipeBlock.NodePipe.END) && direction.equals(Direction.UP)) { - AbstractPipeBlockEntity.moveFluidWithHeightCheck( - level, pos, Direction.UP, pos.relative(Direction.UP), Direction.DOWN); + BlockPos neighborPos = pos.relative(Direction.UP); + if (level.getBlockState(neighborPos).getBlock() instanceof PumpBlock) { + PipeEnd pumpEnd = AbstractPipeBlockEntity.getPipeEnd(level, neighborPos, Direction.UP); + if (pumpEnd != null) { + AbstractPipeBlockEntity.moveFluidWithHeightCheck( + level, + pos, + Direction.UP, + pumpEnd.pos(), + pumpEnd.direction(), + pumpEnd.effectiveHeight() + ); + } + } else { + AbstractPipeBlockEntity.moveFluidWithHeightCheck( + level, + pos, + Direction.UP, + pos.relative(Direction.UP), + Direction.DOWN, + 0 + ); + } } - // END + DOWN:向下方排液 + // END + DOWN:向下方排液(若端头指向泵则透传追踪) if (value.equals(PipeBlock.NodePipe.END) && direction.equals(Direction.DOWN)) { - AbstractPipeBlockEntity.moveFluidWithHeightCheck( - level, pos.relative(Direction.DOWN), Direction.UP, pos, Direction.DOWN); + BlockPos neighborPos = pos.relative(Direction.DOWN); + if (level.getBlockState(neighborPos).getBlock() instanceof PumpBlock) { + PipeEnd pumpEnd = AbstractPipeBlockEntity.getPipeEnd(level, neighborPos, Direction.DOWN); + if (pumpEnd != null) { + AbstractPipeBlockEntity.moveFluidWithHeightCheck( + level, + pos.relative(Direction.DOWN), + Direction.UP, + pumpEnd.pos(), + pumpEnd.direction(), + pumpEnd.effectiveHeight() + ); + } + } else { + AbstractPipeBlockEntity.moveFluidWithHeightCheck( + level, + pos.relative(Direction.DOWN), + Direction.UP, + pos, + Direction.DOWN, + 0 + ); + } } if (!value.equals(PipeBlock.NodePipe.PIPE)) { @@ -148,7 +184,7 @@ public static void tick(Level level, BlockPos pos, BlockState state) { if (pipeEnd == null) { continue; } - pipeEnds.add(new EndAndDirection(pipeEnd, direction)); + pipeEnds.add(new EndAndDirection(pipeEnd, direction, pipeEnd.effectiveHeight())); } if (pipeEnds.isEmpty()) { @@ -162,7 +198,8 @@ public static void tick(Level level, BlockPos pos, BlockState state) { pos.relative(endAndDirection.direction()), endAndDirection.direction().getOpposite(), endAndDirection.end().pos(), - endAndDirection.end().direction() + endAndDirection.end().direction(), + endAndDirection.effectiveHeight() ); } } @@ -170,6 +207,6 @@ public static void tick(Level level, BlockPos pos, BlockState state) { /** * PipeEnd + 方向对 */ - record EndAndDirection(PipeEnd end, Direction direction) { + record EndAndDirection(PipeEnd end, Direction direction, int effectiveHeight) { } } diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PumpBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PumpBlockEntity.java new file mode 100644 index 0000000000..80c882f404 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/fluid/PumpBlockEntity.java @@ -0,0 +1,163 @@ +package dev.dubhe.anvilcraft.block.entity.fluid; + +import dev.dubhe.anvilcraft.api.power.IPowerConsumer; +import dev.dubhe.anvilcraft.api.power.PowerGrid; +import dev.dubhe.anvilcraft.block.fluid.PipeBlock; +import dev.dubhe.anvilcraft.block.fluid.PumpBlock; +import dev.dubhe.anvilcraft.block.state.Orientation; +import lombok.Getter; +import lombok.Setter; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; + +import javax.annotation.Nullable; + +/** + * 泵的 BlockEntity。消费 32kW 电力,提供输入端 +10 / 输出端 -10 的等效高度偏移。 + * + *

工作状态判定: + *

+ * + *

方向映射:{@link dev.dubhe.anvilcraft.block.state.Orientation#getDirection()} 返回输出方向。 + * 输入端为该方向的反方向。泵的输出端高度降低,输入端高度抬升。 + */ +@Getter +@Setter +public class PumpBlockEntity extends AbstractPipeBlockEntity implements IPowerConsumer { + private static final int PUMP_POWER = 32; // 32 kW 电力消耗 + public static final int PUMP_HEADLIFT = 10; // 10 米扬程 + + private @Nullable PowerGrid grid; + private boolean working; + + public PumpBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + public static PumpBlockEntity create(BlockEntityType type, BlockPos pos, BlockState state) { + return new PumpBlockEntity(type, pos, state); + } + + @Override + public int getInputPower() { + return PUMP_POWER; + } + + @Override + public @Nullable Level getCurrentLevel() { + return getLevel(); + } + + @Override + public BlockPos getPos() { + return getBlockPos(); + } + + /** + * 泵是否能实际泵送流体(启用 + 电网有电) + */ + public boolean canPump() { + return this.working && this.grid != null && this.grid.isWorking(); + } + + // ---- NBT ---- + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + tag.putBoolean("Working", working); + } + + @Override + public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + working = tag.getBoolean("Working"); + } + + @Override + public CompoundTag getUpdateTag(HolderLookup.Provider registries) { + CompoundTag tag = super.getUpdateTag(registries); + tag.putBoolean("Working", working); + return tag; + } + + @Nullable + @Override + public Packet getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + // ---- Tick ---- + + /** + * Per-tick:刷新电力/红石/过载状态,更新 heightBonus,并执行主动流体中转。 + *

+ */ + public static void tick(Level level, BlockPos pos, BlockState state, PumpBlockEntity entity) { + if (level.isClientSide()) { + return; + } + + // 刷新电网过载状态到 blockstate + entity.flushState(level, pos); + // flushState 通过 setBlockAndUpdate 修改了 blockstate,需重读 + BlockState updatedState = level.getBlockState(pos); + + boolean powered = updatedState.getValue(PumpBlock.POWERED); + boolean overload = updatedState.getValue(PumpBlock.OVERLOAD); + + // working = 泵处于启用状态(控制动画和流体开关) + boolean wasWorking = entity.working; + entity.working = !powered && !overload; + + if (entity.working != wasWorking) { + entity.setChanged(); + if (!level.isClientSide()) { + entity.sendUpdate(); + } + } + Orientation orientation = updatedState.getValue(PumpBlock.ORIENTATION); + Direction sourceDir = orientation.getDirection(); + BlockPos sourcePos = pos.relative(sourceDir); + if (level.getBlockState(sourcePos).getBlock() instanceof PipeBlock || !entity.canPump()) { + return; + } + Direction targetCurDir = sourceDir.getOpposite(); + IFluidHandler fluidHandler = level.getCapability(Capabilities.FluidHandler.BLOCK, sourcePos, targetCurDir); + if (fluidHandler == null) return; + PipeEnd pumpEnd = getPipeEnd(level, pos, sourceDir); + BlockPos targetCurPos = pos; + int effectiveHeight = 0; + if (pumpEnd != null) { + targetCurPos = pumpEnd.pos(); + targetCurDir = pumpEnd.direction(); + effectiveHeight = pumpEnd.effectiveHeight(); + } + AbstractPipeBlockEntity.moveFluidWithHeightCheck( + level, + pos, + sourceDir, + targetCurPos, + targetCurDir, + effectiveHeight + ); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeBlock.java index 13558d6e15..2ca6e15a2c 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeBlock.java @@ -233,6 +233,22 @@ public static boolean isFluidHandler(Level level, BlockPos pos) { return level.getCapability(Capabilities.FluidHandler.BLOCK, pos, state, be, null) != null; } + /** + * 检查指定位置是否为流体处理器(通过 NeoForge Capability 系统)或泵。 + * + * @param level 世界 + * @param pos 位置 + * @return 该位置是否提供 {@link net.neoforged.neoforge.fluids.capability.IFluidHandler} 或是泵 + */ + public static boolean isFluidHandlerOrPump(Level level, BlockPos pos) { + BlockState state = level.getBlockState(pos); + if (state.getBlock() instanceof PumpBlock) { + return true; + } + BlockEntity be = level.getBlockEntity(pos); + return level.getCapability(Capabilities.FluidHandler.BLOCK, pos, state, be, null) != null; + } + /** * 检查指定方向的邻居是否被"占用"(有管道对准 或 是流体处理器)。 * 用于判断管道端头是否应该打开(无端头连接)。 diff --git a/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeCornerBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeCornerBlock.java index 51f6fb0c57..a67a5f6503 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeCornerBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeCornerBlock.java @@ -92,8 +92,9 @@ protected void neighborChanged( } if (!corner.containsDirection(neighborDir)) { - // 非弯管方向(侧面):有对准的管道 → 转节点 - if (isNeighborPipeToward(level, pos, neighborDir)) { + // 非弯管方向(侧面):有对准的管道或泵 → 转节点 + BlockState neighborState = level.getBlockState(neighborPos); + if (isNeighborPipeToward(level, pos, neighborDir) || neighborState.getBlock() instanceof PumpBlock) { BlockState nodeState = ModBlocks.PIPE_NODE.get().defaultBlockState().setValue(WATERLOGGED, state.getValue(WATERLOGGED)); for (Direction dir : Direction.values()) { nodeState = nodeState.setValue(getPropertyForDirection(dir), PipeNodeBlock.evaluateNeighbor(level, pos, dir)); diff --git a/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeNodeBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeNodeBlock.java index f255be034c..b4565beef6 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeNodeBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeNodeBlock.java @@ -144,7 +144,7 @@ protected void neighborChanged( * 评估指定方向邻居的连接状态。 * * @@ -159,7 +159,7 @@ public static NodePipe evaluateNeighbor(Level level, BlockPos pos, Direction dir if (neighborState.getBlock() instanceof PipeBlock && hasConnectionToward(neighborState, dir.getOpposite())) { return NodePipe.PIPE; } - if (isFluidHandler(level, neighborPos)) { + if (isFluidHandler(level, neighborPos) || neighborState.getBlock() instanceof PumpBlock) { return NodePipe.END; } return NodePipe.NONE; diff --git a/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeStraightBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeStraightBlock.java index c17ef83951..b60a6da74a 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeStraightBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PipeStraightBlock.java @@ -112,9 +112,11 @@ protected void neighborChanged( } if (neighborDir.getAxis() != axis) { - // 侧面连接:检查邻居管道是否对准本方块 + // 侧面连接:检查邻居管道是否对准本方块,或邻居是泵 + BlockState neighborState = level.getBlockState(neighborPos); boolean neighborIsPipeToward = isNeighborPipeToward(level, pos, neighborDir); - if (!neighborIsPipeToward) { + boolean neighborIsPump = neighborState.getBlock() instanceof PumpBlock; + if (!neighborIsPipeToward && !neighborIsPump) { return; } diff --git a/src/main/java/dev/dubhe/anvilcraft/block/fluid/PumpBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PumpBlock.java new file mode 100644 index 0000000000..4aaf74b7c8 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/block/fluid/PumpBlock.java @@ -0,0 +1,237 @@ +package dev.dubhe.anvilcraft.block.fluid; + +import com.mojang.serialization.MapCodec; +import dev.dubhe.anvilcraft.api.hammer.IHammerChangeable; +import dev.dubhe.anvilcraft.api.hammer.IHammerRemovable; +import dev.dubhe.anvilcraft.api.power.IPowerComponent; +import dev.dubhe.anvilcraft.block.better.BetterBaseEntityBlock; +import dev.dubhe.anvilcraft.block.entity.fluid.PumpBlockEntity; +import dev.dubhe.anvilcraft.block.state.Orientation; +import dev.dubhe.anvilcraft.init.block.ModBlockEntities; +import dev.dubhe.anvilcraft.init.block.ModBlocks; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.Nullable; + +/** + * 泵(Pump),管道系统的主动流体输送设备。 + * 消耗 32kW 电力,输入端等效高度 +10,输出端 -10。 + * 12 向放置({@link Orientation}),铁砧锤右键反转方向,红石可关闭。 + */ +public class PumpBlock extends BetterBaseEntityBlock implements IHammerRemovable, IHammerChangeable { + public static final EnumProperty ORIENTATION = EnumProperty.create("orientation", Orientation.class); + public static final BooleanProperty POWERED = BlockStateProperties.POWERED; + public static final BooleanProperty OVERLOAD = IPowerComponent.OVERLOAD; + + /** + * 主体碰撞箱 — 沿 Z 轴延伸(NORTH_UP / SOUTH_UP) + */ + private static final VoxelShape SHAPE_Z = box(3, 3, 0, 13, 13, 16); + /** + * 主体碰撞箱 — 沿 X 轴延伸(WEST_UP / EAST_UP) + */ + private static final VoxelShape SHAPE_X = box(0, 3, 3, 16, 13, 13); + /** + * 主体碰撞箱 — 沿 Y 轴延伸(UP_* / DOWN_*) + */ + private static final VoxelShape SHAPE_Y = box(3, 0, 3, 13, 16, 13); + + public PumpBlock(Properties properties) { + super(properties); + registerDefaultState(stateDefinition.any() + .setValue(ORIENTATION, Orientation.NORTH_UP) + .setValue(POWERED, false) + .setValue(OVERLOAD, false)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(ORIENTATION, POWERED, OVERLOAD); + } + + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + /** + * 根据朝向返回旋转后的主体碰撞箱 + */ + @Override + public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext ctx) { + return switch (state.getValue(ORIENTATION).getDirection().getAxis()) { + case X -> SHAPE_X; + case Y -> SHAPE_Y; + default -> SHAPE_Z; + }; + } + + @Override + protected MapCodec codec() { + return simpleCodec(PumpBlock::new); + } + + /** + * 放置时根据玩家视线和 Shift 计算朝向。 + *
    + *
  • 水平摆放:默认输出端朝向目标方块,Shift 反向(输出端指向玩家)
  • + *
  • 垂直摆放:默认输出端朝向上方/下方,Shift 反转垂直方向(UP ↔ DOWN)
  • + *
+ */ + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + Direction lookDir = context.getNearestLookingDirection(); + Direction horizontalDir = context.getHorizontalDirection(); + Player player = context.getPlayer(); + boolean shiftDown = player != null && player.isShiftKeyDown(); + + if (lookDir.getAxis() == Direction.Axis.Y) { + // 垂直摆放:Shift 反转垂直方向(UP ↔ DOWN),水平方向跟随玩家朝向 + if (!shiftDown) { + lookDir = lookDir.getOpposite(); + } + } else { + // 水平摆放:默认输出端朝向目标方块,Shift 反向(输出端指向玩家) + if (!shiftDown) { + horizontalDir = horizontalDir.getOpposite(); + } + } + + Orientation orientation = switch (lookDir) { + case UP -> switch (horizontalDir) { + case SOUTH -> Orientation.UP_SOUTH; + case WEST -> Orientation.UP_WEST; + case EAST -> Orientation.UP_EAST; + default -> Orientation.UP_NORTH; + }; + case DOWN -> switch (horizontalDir) { + case SOUTH -> Orientation.DOWN_SOUTH; + case WEST -> Orientation.DOWN_WEST; + case EAST -> Orientation.DOWN_EAST; + default -> Orientation.DOWN_NORTH; + }; + default -> switch (horizontalDir) { + case SOUTH -> Orientation.SOUTH_UP; + case WEST -> Orientation.WEST_UP; + case EAST -> Orientation.EAST_UP; + default -> Orientation.NORTH_UP; + }; + }; + + return defaultBlockState().setValue(ORIENTATION, orientation); + } + + /** + * 放置后将侧面连接的直管/弯管转为三通节点,使其能正确吸附管道。 + */ + @Override + public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { + super.setPlacedBy(level, pos, state, placer, stack); + if (level.isClientSide) return; + + for (Direction dir : Direction.values()) { + BlockPos neighborPos = pos.relative(dir); + BlockState neighborState = level.getBlockState(neighborPos); + if (neighborState.getBlock() instanceof PipeStraightBlock) { + Direction.Axis pipeAxis = neighborState.getValue(PipeBlock.AXIS); + // 泵贴在直管侧面 → 将直管转为三通节点 + if (dir.getAxis() != pipeAxis) { + convertPipeToNode(level, neighborPos, neighborState); + } + } else if (neighborState.getBlock() instanceof PipeCornerBlock) { + PipeBlock.CornerEnded corner = neighborState.getValue(PipeBlock.CORNER_ENDED); + // 泵贴在弯管非拐角方向的侧面 → 将弯管转为三通节点 + if (!corner.containsDirection(dir.getOpposite())) { + convertPipeToNode(level, neighborPos, neighborState); + } + } + } + } + + /** + * 将直管或弯管转为三通节点,扫描全部六个方向重新计算连接状态。 + */ + private void convertPipeToNode(Level level, BlockPos pos, BlockState state) { + BlockState nodeState = ModBlocks.PIPE_NODE.get() + .defaultBlockState() + .setValue(PipeBlock.WATERLOGGED, state.getValue(PipeBlock.WATERLOGGED)); + for (Direction dir : Direction.values()) { + nodeState = nodeState.setValue( + PipeBlock.getPropertyForDirection(dir), + PipeNodeBlock.evaluateNeighbor(level, pos, dir) + ); + } + level.setBlockAndUpdate(pos, nodeState); + } + + /** + * 红石信号更新 + */ + @Override + public void neighborChanged(BlockState state, Level level, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) { + if (level.isClientSide) return; + boolean hasSignal = level.hasNeighborSignal(pos); + if (hasSignal != state.getValue(POWERED)) { + level.setBlock(pos, state.setValue(POWERED, hasSignal), 2); + } + } + + /** + * 铁砧锤反转方向 + */ + @Override + public boolean change(Player player, BlockPos blockPos, Level level, ItemStack anvilHammer) { + BlockState state = level.getBlockState(blockPos); + level.setBlockAndUpdate(blockPos, state.setValue(ORIENTATION, state.getValue(ORIENTATION).opposite())); + return true; + } + + @Override + public @Nullable Property getChangeableProperty(BlockState state) { + return ORIENTATION; + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(ORIENTATION, state.getValue(ORIENTATION).rotate(rotation)); + } + + @Override + public @Nullable PumpBlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return ModBlockEntities.PUMP.get().create(pos, state); + } + + @Override + @Nullable + public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType type) { + if (level.isClientSide()) return null; + return BaseEntityBlock.createTickerHelper(type, ModBlockEntities.PUMP.get(), PumpBlockEntity::tick); + } + + @Override + public boolean checkBlockState(BlockState blockState) { + return false; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/block/item/PipeBlockItem.java b/src/main/java/dev/dubhe/anvilcraft/block/item/PipeBlockItem.java index 3fcd14c374..ccdff70db9 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/item/PipeBlockItem.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/item/PipeBlockItem.java @@ -87,9 +87,9 @@ private boolean tryConnectAdjacent(BlockPlaceContext context) { BlockState placeState = level.getBlockState(placePos); boolean targetIsPipe = targetState.getBlock() instanceof PipeBlock; - boolean targetIsFluid = PipeBlock.isFluidHandler(level, targetPos); + boolean targetIsFluid = PipeBlock.isFluidHandlerOrPump(level, targetPos); boolean placeIsPipe = placeState.getBlock() instanceof PipeBlock; - boolean placeIsFluid = PipeBlock.isFluidHandler(level, placePos); + boolean placeIsFluid = PipeBlock.isFluidHandlerOrPump(level, placePos); // 双方至少有一方是管道/IFluidHandler 且另一方也是 if ((!targetIsPipe && !targetIsFluid) || (!placeIsPipe && !placeIsFluid)) { @@ -375,7 +375,7 @@ protected BlockState getPlacementState(BlockPlaceContext context) { boolean shiftDown = player != null && player.isShiftKeyDown(); boolean clickedOnPipe = targetBlock instanceof PipeBlock; - boolean clickedOnFluidHandler = PipeBlock.isFluidHandler(level, targetPos); + boolean clickedOnFluidHandler = PipeBlock.isFluidHandlerOrPump(level, targetPos); // 视线模式 if (shiftDown || (!clickedOnPipe && !clickedOnFluidHandler)) { diff --git a/src/main/java/dev/dubhe/anvilcraft/client/event/RegisterAdditionalEventListener.java b/src/main/java/dev/dubhe/anvilcraft/client/event/RegisterAdditionalEventListener.java index 6006af59f2..7fed12f92b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/event/RegisterAdditionalEventListener.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/event/RegisterAdditionalEventListener.java @@ -71,6 +71,8 @@ public static void registerModels(ModelEvent.RegisterAdditional event) { event.register(ModelResourceLocation.standalone(AnvilCraft.of("block/smart_block_placer_forearm"))); event.register(ModelResourceLocation.standalone(AnvilCraft.of("block/smart_block_placer_claw"))); event.register(ModelResourceLocation.standalone(AnvilCraft.of("block/smart_block_placer_claw_open"))); + event.register(ModelResourceLocation.standalone(AnvilCraft.of("block/pump_piston_1"))); + event.register(ModelResourceLocation.standalone(AnvilCraft.of("block/pump_piston_2"))); // Special celestial body models event.register(ModelResourceLocation.standalone(AnvilCraft.of("block/celestial_body/planet_overworld"))); diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/PumpBlockEntityRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/PumpBlockEntityRenderer.java new file mode 100644 index 0000000000..3ef41ef0d5 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/PumpBlockEntityRenderer.java @@ -0,0 +1,116 @@ +package dev.dubhe.anvilcraft.client.renderer.blockentity; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.block.entity.fluid.PumpBlockEntity; +import dev.dubhe.anvilcraft.block.fluid.PumpBlock; +import dev.dubhe.anvilcraft.block.state.Orientation; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.world.level.block.state.BlockState; + +/** + * 泵的方块实体渲染器。 + * 在工作状态时渲染两个活塞模型(pump_piston_1, pump_piston_2), + * 交替上下运动,运动速度与流体传输量正相关。 + * + *

基础模型(pump_base/pump_off/pump_overload)由 blockstate 系统渲染, + * 本渲染器仅负责动画活塞部分。 + */ +public class PumpBlockEntityRenderer implements BlockEntityRenderer { + + private static final ModelResourceLocation PUMP_PISTON_1 = + ModelResourceLocation.standalone(AnvilCraft.of("block/pump_piston_1")); + private static final ModelResourceLocation PUMP_PISTON_2 = + ModelResourceLocation.standalone(AnvilCraft.of("block/pump_piston_2")); + + /** + * 活塞最大位移(单位:方块,2/16 = 2 像素) + */ + private static final float MAX_PISTON_OFFSET = 1.5f / 16.0f; + + /** + * 最大传输速率(mB/tick),用于归一化动画速度 + */ + private static final float MAX_TRANSFER_RATE = 500.0f; + + @SuppressWarnings("unused") + public PumpBlockEntityRenderer(BlockEntityRendererProvider.Context context) { + } + + @Override + public void render( + PumpBlockEntity blockEntity, + float partialTick, + PoseStack poseStack, + MultiBufferSource buffer, + int packedLight, + int packedOverlay + ) { + if (!blockEntity.isWorking()) return; + + BlockState state = blockEntity.getBlockState(); + if (!(state.getBlock() instanceof PumpBlock)) return; + float speed = 1.0f; + long gameTime = blockEntity.getLevel().getGameTime(); + float cycle = ((gameTime + partialTick) * speed) % 20.0f / 20.0f; + + Orientation orientation = state.getValue(PumpBlock.ORIENTATION); + // 应用与 blockstate 相同的旋转,使活塞坐标系与模型对齐 + poseStack.pushPose(); + poseStack.translate(0.5, 0.5, 0.5); + // blockstate Y+ 方向与 JOML 相反,需取反 + // 顺序:先 X 后 Y(mulPose 后乘,先 Y 则 X 对向量先生效) + poseStack.mulPose(Axis.YP.rotationDegrees(-orientation.getYRotation())); + poseStack.mulPose(Axis.XP.rotationDegrees(orientation.getXRotation())); + poseStack.translate(-0.5, -0.5, -0.5); + + poseStack.translate(0, -2.0f / 16.0f, 0); + + // 活塞沿 Y 轴正弦/余弦交替运动,无死区 + float angle = cycle * 2.0f * (float) Math.PI; + float piston1Offset = (float) Math.sin(angle) * MAX_PISTON_OFFSET; + + BakedModel piston1 = Minecraft.getInstance().getModelManager().getModel(PUMP_PISTON_1); + poseStack.pushPose(); + poseStack.translate(0, piston1Offset, 0); + renderPistonModel(poseStack, buffer, piston1, packedLight, packedOverlay); + poseStack.popPose(); + + float piston2Offset = (float) Math.cos(angle) * MAX_PISTON_OFFSET; + BakedModel piston2 = Minecraft.getInstance().getModelManager().getModel(PUMP_PISTON_2); + poseStack.pushPose(); + poseStack.translate(0, piston2Offset, 0); + renderPistonModel(poseStack, buffer, piston2, packedLight, packedOverlay); + poseStack.popPose(); + + poseStack.popPose(); + } + + private void renderPistonModel( + PoseStack poseStack, + MultiBufferSource buffer, + BakedModel model, + int packedLight, + int packedOverlay + ) { + Minecraft.getInstance() + .getBlockRenderer() + .getModelRenderer() + .renderModel( + poseStack.last(), + buffer.getBuffer(RenderType.cutout()), + null, + model, + 1.0f, 1.0f, 1.0f, + packedLight, + packedOverlay + ); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumBlockRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumBlockRecipeLoader.java index b7d3ec0213..1493e76b2d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumBlockRecipeLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumBlockRecipeLoader.java @@ -2140,4 +2140,23 @@ public static void infiniteCollector(DataGenContext AnvilCraftDatagen.has(ModItems.TRANSCENDIUM_INGOT)) .save(provider); } + + public static void pump(DataGenContext ctx, RegistrumRecipeProvider provider) { + ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ctx.get().asItem(), 2) + .pattern("PCP") + .pattern("P P") + .pattern(" ") + .define('P', Blocks.PISTON) + .define('C', ModBlocks.PIPE_STRAIGHT.asItem()) + .group(ctx.getId().toString()) + .unlockedBy( + AnvilCraftDatagen.hasItem(Blocks.PISTON.asItem()), + RegistrumRecipeProvider.has(Blocks.PISTON) + ) + .unlockedBy( + AnvilCraftDatagen.hasItem(ModBlocks.PIPE_STRAIGHT.asItem()), + RegistrumRecipeProvider.has(ModBlocks.PIPE_STRAIGHT) + ) + .save(provider); + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumItemRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumItemRecipeLoader.java index 5f07d9140c..2a7d3386a3 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumItemRecipeLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumItemRecipeLoader.java @@ -1365,4 +1365,5 @@ public static void pipe(DataGenContext ctx, RegistrumR ) .save(provider); } + } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java index 46e9a2c510..aedec51636 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java @@ -69,6 +69,7 @@ import dev.dubhe.anvilcraft.block.entity.batch.BatchCutterBlockEntity; import dev.dubhe.anvilcraft.block.entity.fluid.PipeBlockEntity; import dev.dubhe.anvilcraft.block.entity.fluid.PipeNodeBlockEntity; +import dev.dubhe.anvilcraft.block.entity.fluid.PumpBlockEntity; import dev.dubhe.anvilcraft.block.entity.heatable.GlowingBlockEntity; import dev.dubhe.anvilcraft.block.entity.heatable.HeatedBlockEntity; import dev.dubhe.anvilcraft.block.entity.heatable.IncandescentBlockEntity; @@ -101,6 +102,7 @@ import dev.dubhe.anvilcraft.client.renderer.blockentity.LargeFluidTankBlockEntityRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.LaserBlockRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.PlasmaJetsRenderer; +import dev.dubhe.anvilcraft.client.renderer.blockentity.PumpBlockEntityRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.SmartBlockPlacerRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.TeslaTowerRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.TradingStationBlockEntityRenderer; @@ -491,6 +493,12 @@ public class ModBlockEntities { .renderer(() -> LaserBlockRenderer::new) .register(); + public static final BlockEntityEntry PUMP = REGISTRUM + .blockEntity("pump", PumpBlockEntity::create) + .validBlock(ModBlocks.PUMP) + .renderer(() -> PumpBlockEntityRenderer::new) + .register(); + public static final BlockEntityEntry WIP_BLOCK = REGISTRUM .blockEntity("wip_block", WipBlockEntity::new) .validBlock(ModBlocks.WIP_BLOCK) diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java index 84821ed1e1..28c4cef950 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java @@ -152,10 +152,11 @@ import dev.dubhe.anvilcraft.block.cfa.item.CelestialForgingAnvilAmplifierBlockItem; import dev.dubhe.anvilcraft.block.cfa.item.CelestialForgingAnvilBlockItem; import dev.dubhe.anvilcraft.block.cfa.item.CelestialForgingAnvilInterfaceBlockItem; +import dev.dubhe.anvilcraft.block.cfa.item.CelestialForgingAnvilPortalBlockItem; import dev.dubhe.anvilcraft.block.fluid.PipeCornerBlock; import dev.dubhe.anvilcraft.block.fluid.PipeNodeBlock; import dev.dubhe.anvilcraft.block.fluid.PipeStraightBlock; -import dev.dubhe.anvilcraft.block.cfa.item.CelestialForgingAnvilPortalBlockItem; +import dev.dubhe.anvilcraft.block.fluid.PumpBlock; import dev.dubhe.anvilcraft.block.heatable.GlowingBlock; import dev.dubhe.anvilcraft.block.heatable.HeatedBlock; import dev.dubhe.anvilcraft.block.heatable.IncandescentBlock; @@ -208,6 +209,7 @@ import dev.dubhe.anvilcraft.block.state.Cube3x3PartHalf; import dev.dubhe.anvilcraft.block.state.DirectionCube3x3PartHalf; import dev.dubhe.anvilcraft.block.state.FragmentationDegree; +import dev.dubhe.anvilcraft.block.state.Orientation; import dev.dubhe.anvilcraft.block.state.Vertical3PartHalf; import dev.dubhe.anvilcraft.block.state.Vertical4PartHalf; import dev.dubhe.anvilcraft.data.generator.PipeBlockStateGenerator; @@ -216,6 +218,7 @@ import dev.dubhe.anvilcraft.init.item.ModItemGroups; import dev.dubhe.anvilcraft.init.item.ModItemTags; import dev.dubhe.anvilcraft.init.item.ModItems; +import dev.dubhe.anvilcraft.item.SingularityCrystalItem; import dev.dubhe.anvilcraft.item.TeslaTowerItem; import dev.dubhe.anvilcraft.item.property.component.OverLimitItemContainerContents; import dev.dubhe.anvilcraft.util.DangerUtil; @@ -276,10 +279,7 @@ import static dev.dubhe.anvilcraft.api.power.IPowerComponent.OVERLOAD; import static dev.dubhe.anvilcraft.api.power.IPowerComponent.SWITCH; -@SuppressWarnings({ - "unused", - "CodeBlock2Expr" -}) +@SuppressWarnings({"unused", "CodeBlock2Expr"}) public class ModBlocks { static { REGISTRUM.defaultCreativeTab(ModItemGroups.ANVILCRAFT_FUNCTION_BLOCK.getKey()); @@ -998,9 +998,9 @@ public class ModBlocks { .blockstate((ctx, provider) -> { provider.getVariantBuilder(ctx.get()).forAllStates(state -> { Direction facing = state.getValue(HorizontalDirectionalBlock.FACING); - boolean upsideDown = state.getValue(dev.dubhe.anvilcraft.block.SmartBlockPlacerBlock.UPSIDE_DOWN); - boolean powered = state.getValue(dev.dubhe.anvilcraft.block.SmartBlockPlacerBlock.POWERED); - boolean overload = state.getValue(dev.dubhe.anvilcraft.block.SmartBlockPlacerBlock.OVERLOAD); + boolean upsideDown = state.getValue(SmartBlockPlacerBlock.UPSIDE_DOWN); + boolean powered = state.getValue(SmartBlockPlacerBlock.POWERED); + boolean overload = state.getValue(SmartBlockPlacerBlock.OVERLOAD); // 根据状态选择模型 String modelName; @@ -1026,7 +1026,7 @@ public class ModBlocks { rotation = (rotation + 180) % 360; } - return net.neoforged.neoforge.client.model.generators.ConfiguredModel.builder() + return ConfiguredModel.builder() .modelFile(model) .rotationX(upsideDown ? 180 : 0) .rotationY(rotation) @@ -1532,9 +1532,10 @@ public class ModBlocks { .loot((tables, block) -> { // Generate empty loot table (rolls=0) so datagen doesn't break. // Actual drop (with NBT) is handled manually in onRemove. - tables.add(block, LootTable.lootTable() - .withPool(LootPool.lootPool() - .setRolls(ConstantValue.exactly(0.0f)))); + tables.add( + block, + LootTable.lootTable().withPool(LootPool.lootPool().setRolls(ConstantValue.exactly(0.0f))) + ); }) .tag((BlockTags.MINEABLE_WITH_PICKAXE), BlockTags.WITHER_IMMUNE, BlockTags.DRAGON_IMMUNE, ModBlockTags.COLLISION_IMMUNE) .item(CelestialForgingAnvilBlockItem::new) @@ -3875,7 +3876,7 @@ private static BlockEntry registerCementLiquidBlock(Color color) { .strength(50F, 1200.0F) .requiresCorrectToolForDrops()) .tag(BlockTags.MINEABLE_WITH_PICKAXE, ModBlockTags.NEEDS_TRANSCENDIUM_TOOL, ModBlockTags.COLLISION_IMMUNE) - .item(dev.dubhe.anvilcraft.item.SingularityCrystalItem::new) + .item(SingularityCrystalItem::new) .initialProperties(() -> new Item.Properties().fireResistant().stacksTo(1)) .tag(ModItemTags.EXPLOSION_PROOF) .build() @@ -4204,6 +4205,38 @@ private static BlockEntry registerCementLiquidBlock(Color color) { .tag(BlockTags.MINEABLE_WITH_PICKAXE) .register(); + public static final BlockEntry PUMP = REGISTRUM.block("pump", PumpBlock::new) + .initialProperties(() -> Blocks.IRON_BLOCK) + .properties(p -> p.noOcclusion().sound(SoundType.METAL)) + .blockstate((ctx, provider) -> { + provider.getVariantBuilder(ctx.get()).forAllStates(state -> { + boolean powered = state.getValue(PumpBlock.POWERED); + boolean overload = state.getValue(PumpBlock.OVERLOAD); + Orientation orientation = state.getValue(PumpBlock.ORIENTATION); + + String modelName; + if (overload) { + modelName = "block/pump_overload"; + } else if (powered) { + modelName = "block/pump_off"; + } else { + modelName = "block/pump_base"; + } + + var model = provider.models().getExistingFile(AnvilCraft.of(modelName)); + + return ConfiguredModel.builder() + .modelFile(model) + .rotationX((int) -orientation.getXRotation()) + .rotationY((int) orientation.getYRotation()) + .build(); + }); + }) + .simpleItem() + .recipe(RegistrumBlockRecipeLoader::pump) + .tag(BlockTags.MINEABLE_WITH_PICKAXE) + .register(); + public static void register() { } diff --git a/src/main/resources/assets/anvilcraft/models/block/pump.json b/src/main/resources/assets/anvilcraft/models/block/pump.json index 61bf853de0..8e6ee499a0 100644 --- a/src/main/resources/assets/anvilcraft/models/block/pump.json +++ b/src/main/resources/assets/anvilcraft/models/block/pump.json @@ -166,40 +166,22 @@ ], "display": { "thirdperson_righthand": { - "rotation": [75, 45, 0], - "translation": [0, 2.5, 0], - "scale": [0.2, 0.2, 0.2] + "translation": [0, -2, 0], + "scale": [0.5, 0.5, 0.5] }, "thirdperson_lefthand": { - "rotation": [75, 45, 0], - "translation": [0, 2.5, 0], - "scale": [0.2, 0.2, 0.2] - }, - "firstperson_righthand": { - "rotation": [0, -135, 0], - "translation": [3.75, 0, 0], - "scale": [0.2, 0.2, 0.2] - }, - "firstperson_lefthand": { - "rotation": [0, -135, 0], - "translation": [3.75, 0, 0], - "scale": [0.2, 0.2, 0.2] - }, - "ground": { - "translation": [0, 4, 0], - "scale": [0.2, 0.2, 0.2] - }, - "gui": { - "rotation": [30, -135, 0], - "translation": [0, -0.25, 0], - "scale": [0.24, 0.24, 0.24] + "translation": [0, -2, 0], + "scale": [0.5, 0.5, 0.5] }, "head": { - "translation": [0, 1, 0], - "scale": [0.6, 0.6, 0.6] + "translation": [0, 0, -14.25] }, "fixed": { - "scale": [0.66, 0.66, 0.66] + "rotation": [90, 0, 0], + "scale": [0.5, 0.5, 0.5] + }, + "on_shelf": { + "rotation": [90, 0, 0] } } } \ No newline at end of file