From 0aa91679e3728d1bc1a328ac2513758de81f71b7 Mon Sep 17 00:00:00 2001 From: xypp <2952795729@qq.com> Date: Sat, 26 Apr 2025 21:02:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81blockup=E5=AF=BB=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + gradle.properties | 2 +- .../maid_useful_task/UsefulTaskExtension.java | 4 +- .../behavior/BlockUpDestroyBehavior.java | 81 ++++++++++ .../behavior/BlockUpPlaceBehavior.java | 109 +++++++++++++ .../behavior/BlockUpScheduleBehavior.java | 60 +++++++ .../behavior/DestoryBlockBehavior.java | 3 +- .../behavior/DestoryBlockMoveBehavior.java | 28 +++- .../behavior/LoopWithTokenBehavior.java | 21 +++ .../behavior/MaidCenterMoveToBlockTask.java | 100 ++++++++++++ .../behavior/PlaceBlockBehavior.java | 2 +- .../behavior/PlaceBlockMoveBehavior.java | 23 ++- .../memory/BlockUpContext.java | 94 +++++++++++ .../memory/TaskRateLimitToken.java | 50 ++++++ .../mixin/MaidCheckPickupItem.java | 19 +++ .../mixin/MaidMoveControlMixin.java | 27 ++++ .../mixin/MaidRunOneMixin.java | 4 +- .../registry/MemoryModuleRegistry.java | 6 + .../task/IMaidBlockDestroyTask.java | 35 ++-- .../task/IMaidBlockPlaceTask.java | 21 +-- .../task/IMaidBlockUpTask.java | 151 ++++++++++++++++++ .../maid_useful_task/task/MaidTreeTask.java | 31 +++- .../maid_useful_task/util/Conditions.java | 14 +- .../maid_useful_task/util/MaidUtils.java | 55 +++++++ .../maid_useful_task/util/MemoryUtil.java | 20 +++ .../maid_useful_task/util/PosUtils.java | 14 ++ .../util/WrappedMaidFakePlayer.java | 36 ++++- .../resources/maid_useful_task.mixins.json | 2 + 28 files changed, 946 insertions(+), 67 deletions(-) create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpDestroyBehavior.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpPlaceBehavior.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpScheduleBehavior.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/behavior/LoopWithTokenBehavior.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/behavior/MaidCenterMoveToBlockTask.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/memory/BlockUpContext.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/memory/TaskRateLimitToken.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidCheckPickupItem.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidMoveControlMixin.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockUpTask.java create mode 100644 src/main/java/studio/fantasyit/maid_useful_task/util/PosUtils.java diff --git a/build.gradle b/build.gradle index 7c09c69..16ad660 100644 --- a/build.gradle +++ b/build.gradle @@ -167,6 +167,7 @@ dependencies { annotationProcessor 'org.spongepowered:mixin:0.8.5:processor' compileOnly fg.deobf("curse.maven:touhou-little-maid-355044:6440955") runtimeOnly fg.deobf("curse.maven:touhou-little-maid-355044:6440955") + runtimeOnly fg.deobf("curse.maven:maid-storage-manager-1210244:6455832") } // This block of code expands all declared replace properties in the specified resource targets. diff --git a/gradle.properties b/gradle.properties index a8219a2..76faa82 100644 --- a/gradle.properties +++ b/gradle.properties @@ -42,7 +42,7 @@ mod_name=maid useful task # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=MIT # The mod version. See https://semver.org/ -mod_version=1.0.0 +mod_version=1.0.1 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/studio/fantasyit/maid_useful_task/UsefulTaskExtension.java b/src/main/java/studio/fantasyit/maid_useful_task/UsefulTaskExtension.java index 9dbf2e2..54ec691 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/UsefulTaskExtension.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/UsefulTaskExtension.java @@ -26,7 +26,9 @@ public class UsefulTaskExtension implements ILittleMaid { public List> getExtraMemoryTypes() { return List.of( MemoryModuleRegistry.DESTROY_TARGET.get(), - MemoryModuleRegistry.PLACE_TARGET.get() + MemoryModuleRegistry.PLACE_TARGET.get(), + MemoryModuleRegistry.BLOCK_UP_TARGET.get(), + MemoryModuleRegistry.RATE_LIMIT_TOKEN.get() ); } }); diff --git a/src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpDestroyBehavior.java b/src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpDestroyBehavior.java new file mode 100644 index 0000000..32723d7 --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpDestroyBehavior.java @@ -0,0 +1,81 @@ +package studio.fantasyit.maid_useful_task.behavior; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.ai.behavior.Behavior; +import net.minecraft.world.entity.ai.memory.MemoryModuleType; +import net.minecraft.world.entity.ai.memory.MemoryStatus; +import net.minecraft.world.level.block.state.BlockState; +import studio.fantasyit.maid_useful_task.memory.BlockUpContext; +import studio.fantasyit.maid_useful_task.task.IMaidBlockUpTask; +import studio.fantasyit.maid_useful_task.util.Conditions; +import studio.fantasyit.maid_useful_task.util.MaidUtils; +import studio.fantasyit.maid_useful_task.util.MemoryUtil; +import studio.fantasyit.maid_useful_task.util.WrappedMaidFakePlayer; + +import java.util.Map; + +public class BlockUpDestroyBehavior extends Behavior { + private BlockUpContext context; + private IMaidBlockUpTask task; + + WrappedMaidFakePlayer fakePlayer; + + public BlockUpDestroyBehavior(Map, MemoryStatus> p_22528_) { + super(p_22528_); + } + + public BlockUpDestroyBehavior() { + super(Map.of(),200); + } + + @Override + protected boolean checkExtraStartConditions(ServerLevel p_22538_, EntityMaid p_22539_) { + if (!MemoryUtil.getBlockUpContext(p_22539_).hasTarget()) return false; + if (MemoryUtil.getBlockUpContext(p_22539_).getStatus() != BlockUpContext.STATUS.DOWN) return false; + return Conditions.hasReachedValidTargetOrReset(p_22539_, 0.8f); + } + + @Override + protected boolean canStillUse(ServerLevel p_22545_, EntityMaid maid, long p_22547_) { + if (MemoryUtil.getBlockUpContext(maid).getStatus() != BlockUpContext.STATUS.DOWN) return false; + return MemoryUtil.getBlockUpContext(maid).isOnLine(maid.blockPosition()) && !maid.blockPosition().equals(context.getStartPos()); + } + + + @Override + protected void start(ServerLevel p_22540_, EntityMaid maid, long p_22542_) { + context = MemoryUtil.getBlockUpContext(maid); + task = (IMaidBlockUpTask) maid.getTask(); + fakePlayer = WrappedMaidFakePlayer.get(maid); + } + + float progress = 0; + + @Override + protected void tick(ServerLevel level, EntityMaid maid, long p_22553_) { + if (!maid.onGround()) { + progress = 0; + } else { + task.swapValidToolToHand(maid); + BlockPos targetPos = maid.blockPosition().below(); + maid.swing(InteractionHand.MAIN_HAND); + MemoryUtil.setLookAt(maid, targetPos); + BlockState targetBlockState = level.getBlockState(targetPos); + float speed = fakePlayer.getMainHandItem().getDestroySpeed(targetBlockState) / fakePlayer.getDigSpeed(targetBlockState, targetPos) / 30; + progress += speed; + if (progress >= 1f) { + MaidUtils.destroyBlock(maid, targetPos); + progress = 0; + } + } + } + @Override + protected void stop(ServerLevel p_22548_, EntityMaid maid, long p_22550_) { + super.stop(p_22548_, maid, p_22550_); + context.setStatus(BlockUpContext.STATUS.IDLE); + MemoryUtil.clearTarget(maid); + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpPlaceBehavior.java b/src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpPlaceBehavior.java new file mode 100644 index 0000000..5a7012f --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpPlaceBehavior.java @@ -0,0 +1,109 @@ +package studio.fantasyit.maid_useful_task.behavior; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.ai.behavior.Behavior; +import net.minecraft.world.entity.ai.memory.MemoryModuleType; +import net.minecraft.world.entity.ai.memory.MemoryStatus; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import studio.fantasyit.maid_useful_task.memory.BlockUpContext; +import studio.fantasyit.maid_useful_task.task.IMaidBlockUpTask; +import studio.fantasyit.maid_useful_task.util.Conditions; +import studio.fantasyit.maid_useful_task.util.MaidUtils; +import studio.fantasyit.maid_useful_task.util.MemoryUtil; + +import java.util.Map; + +public class BlockUpPlaceBehavior extends Behavior { + private BlockUpContext context; + private IMaidBlockUpTask task; + private int tickCount; + + + public BlockUpPlaceBehavior(Map, MemoryStatus> p_22528_) { + super(p_22528_); + } + + public BlockUpPlaceBehavior() { + super(Map.of(), 200); + } + + @Override + protected boolean checkExtraStartConditions(ServerLevel p_22538_, EntityMaid p_22539_) { + if (!MemoryUtil.getBlockUpContext(p_22539_).hasTarget()) return false; + if (MemoryUtil.getBlockUpContext(p_22539_).getStatus() != BlockUpContext.STATUS.UP) return false; + return Conditions.hasReachedValidTargetOrReset(p_22539_, 0.8f); + } + + @Override + protected boolean canStillUse(ServerLevel p_22545_, EntityMaid maid, long p_22547_) { + if (MemoryUtil.getBlockUpContext(maid).getStatus() != BlockUpContext.STATUS.UP) return false; + if (!p_22545_.getBlockState(maid.blockPosition().above().above()).isAir()) return false; + return !(maid.blockPosition().equals(context.getTargetPos()) && maid.onGround()); + } + + + @Override + protected void start(ServerLevel p_22540_, EntityMaid maid, long p_22542_) { + context = MemoryUtil.getBlockUpContext(maid); + task = (IMaidBlockUpTask) maid.getTask(); + tickCount = 0; + } + + protected boolean alignOrTryMove(ServerLevel level, EntityMaid maid) { + AABB boundingBox = maid.getBoundingBox(); + BlockPos startPos = context.getStartPos(); + Vec3 move = maid.getDeltaMovement(); + if (boundingBox.maxX <= startPos.getX() + 1 && boundingBox.maxZ <= startPos.getZ() + 1 + && boundingBox.minX >= startPos.getX() && boundingBox.minZ >= startPos.getZ() + ) { + maid.setDeltaMovement(0, move.y, 0); + return true; + } + + Vec3 dv = startPos.getCenter().subtract(boundingBox.getCenter()).normalize().scale(0.02); + maid.setDeltaMovement(dv.x, move.y, dv.z); + return false; + } + + @Override + protected void tick(ServerLevel level, EntityMaid maid, long p_22553_) { + if (!alignOrTryMove(level, maid)) return; + tickCount += 1; + if (!maid.onGround()) { + task.swapValidItemToHand(maid); + BlockPos pos = maid.blockPosition(); + BlockPos below = pos.below(); + if (context.isOnLine(pos)) + if (below.equals(context.getTargetPos())) + return; + if (level.getBlockState(below).canBeReplaced() && level.getBlockState(pos).canBeReplaced()) { + maid.swing(InteractionHand.MAIN_HAND); + MaidUtils.placeBlock(maid, below); + } + } else { + maid.getJumpControl().jump(); + } + } + + @Override + protected boolean timedOut(long p_22537_) { + return tickCount > 240; + } + + @Override + protected void stop(ServerLevel p_22548_, EntityMaid maid, long p_22550_) { + super.stop(p_22548_, maid, p_22550_); + context.setStatus(BlockUpContext.STATUS.IDLE); + if (!maid.blockPosition().equals(context.getTargetPos())) { + BlockPos startPos = context.getStartPos(); + BlockPos blockPos = maid.blockPosition(); + context.setStartTarget(new BlockPos(blockPos.getX(), startPos.getY(), blockPos.getZ()), blockPos); + } + MemoryUtil.clearTarget(maid); + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpScheduleBehavior.java b/src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpScheduleBehavior.java new file mode 100644 index 0000000..c4a9f2c --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/behavior/BlockUpScheduleBehavior.java @@ -0,0 +1,60 @@ +package studio.fantasyit.maid_useful_task.behavior; + +import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.task.MaidCheckRateTask; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitEntities; +import com.google.common.collect.ImmutableMap; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.ai.behavior.Behavior; +import net.minecraft.world.entity.ai.behavior.BehaviorUtils; +import net.minecraft.world.entity.ai.behavior.BlockPosTracker; +import net.minecraft.world.entity.ai.memory.MemoryModuleType; +import net.minecraft.world.entity.ai.memory.MemoryStatus; +import oshi.util.tuples.Pair; +import studio.fantasyit.maid_useful_task.memory.BlockUpContext; +import studio.fantasyit.maid_useful_task.memory.TaskRateLimitToken; +import studio.fantasyit.maid_useful_task.task.IMaidBlockUpTask; +import studio.fantasyit.maid_useful_task.util.MemoryUtil; + +import java.util.Map; + +public class BlockUpScheduleBehavior extends Behavior { + public BlockUpScheduleBehavior() { + super(ImmutableMap.of(MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT, InitEntities.TARGET_POS.get(), MemoryStatus.VALUE_ABSENT)); + } + + @Override + protected boolean checkExtraStartConditions(ServerLevel p_22538_, EntityMaid p_22539_) { + if (MemoryUtil.getRateLimitToken(p_22539_).isFor(TaskRateLimitToken.Level.L3)) { + return false; + } + return super.checkExtraStartConditions(p_22538_, p_22539_); + } + + @Override + protected void start(ServerLevel p_22540_, EntityMaid maid, long p_22542_) { + BlockUpContext context = MemoryUtil.getBlockUpContext(maid); + IMaidBlockUpTask task = (IMaidBlockUpTask) maid.getTask(); + if (context.hasTarget()) { + if (context.getStatus() != BlockUpContext.STATUS.IDLE && MemoryUtil.getTargetPos(maid) == null) { + context.clearStartTarget(); + } else if (!context.isOnLine(maid.blockPosition()) || context.getStartPos().equals(context.getTargetPos())) { + context.clearStartTarget(); + } else if (context.getStatus() == BlockUpContext.STATUS.IDLE && !context.isTarget(maid.blockPosition()) && context.isOnLine(maid.blockPosition())) { + context.setStartTarget(context.getStartPos(), maid.blockPosition()); + } else if (context.getStatus() == BlockUpContext.STATUS.IDLE && !task.stillValid(maid, maid.blockPosition())) { + maid.getBrain().setMemory(InitEntities.TARGET_POS.get(), new BlockPosTracker(context.getTargetPos())); + context.setStatus(BlockUpContext.STATUS.DOWN); + } + } else { + Pair targetPosBlockUp = task.findTargetPosBlockUp(maid, maid.blockPosition()); + if (targetPosBlockUp != null) { + context.setStartTarget(targetPosBlockUp.getA(), targetPosBlockUp.getB()); + maid.getBrain().setMemory(InitEntities.TARGET_POS.get(), new BlockPosTracker(targetPosBlockUp.getA())); + BehaviorUtils.setWalkAndLookTargetMemories(maid, targetPosBlockUp.getA(), 0.5f, 0); + context.setStatus(BlockUpContext.STATUS.UP); + } + } + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/behavior/DestoryBlockBehavior.java b/src/main/java/studio/fantasyit/maid_useful_task/behavior/DestoryBlockBehavior.java index 5d78eda..444afbc 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/behavior/DestoryBlockBehavior.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/behavior/DestoryBlockBehavior.java @@ -16,6 +16,7 @@ import studio.fantasyit.maid_useful_task.util.Conditions; import studio.fantasyit.maid_useful_task.util.MemoryUtil; import studio.fantasyit.maid_useful_task.util.WrappedMaidFakePlayer; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -47,7 +48,7 @@ public class DestoryBlockBehavior extends Behavior { super.start(p_22540_, maid, p_22542_); BlockTargetMemory blockTargetMemory = MemoryUtil.getDestroyTargetMemory(maid); if (blockTargetMemory != null) { - blockPosSet = blockTargetMemory.getBlockPosSet(); + blockPosSet =new ArrayList<>(blockTargetMemory.getBlockPosSet()); blockPosSet.sort((o1, o2) -> (int) (o1.distSqr(maid.blockPosition()) - o2.distSqr(maid.blockPosition()))); } index = 0; diff --git a/src/main/java/studio/fantasyit/maid_useful_task/behavior/DestoryBlockMoveBehavior.java b/src/main/java/studio/fantasyit/maid_useful_task/behavior/DestoryBlockMoveBehavior.java index 5287aa7..9d8260d 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/behavior/DestoryBlockMoveBehavior.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/behavior/DestoryBlockMoveBehavior.java @@ -7,19 +7,30 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import studio.fantasyit.maid_useful_task.memory.TaskRateLimitToken; import studio.fantasyit.maid_useful_task.task.IMaidBlockDestroyTask; +import studio.fantasyit.maid_useful_task.util.Conditions; import studio.fantasyit.maid_useful_task.util.MemoryUtil; import java.util.List; -public class DestoryBlockMoveBehavior extends MaidMoveToBlockTask { +public class DestoryBlockMoveBehavior extends MaidCenterMoveToBlockTask { private IMaidBlockDestroyTask task; private MaidPathFindingBFS pathfindingBFS; private BlockPos targetPos; List blockPosSet; public DestoryBlockMoveBehavior() { - super(0.5f, 4); + super(0.5f, 7, 8); + } + + + @Override + protected boolean checkExtraStartConditions(ServerLevel p_22538_, EntityMaid p_22539_) { + if (MemoryUtil.getRateLimitToken(p_22539_).isFor(TaskRateLimitToken.Level.L1)) { + return false; + } + return super.checkExtraStartConditions(p_22538_, p_22539_); } @Override @@ -34,7 +45,6 @@ public class DestoryBlockMoveBehavior extends MaidMoveToBlockTask { MemoryUtil.setDestroyTargetMemory(maid, blockPosSet); } } - @Override protected boolean shouldMoveTo(@NotNull ServerLevel serverLevel, @NotNull EntityMaid entityMaid, @NotNull BlockPos blockPos) { if (!task.shouldDestroyBlock(entityMaid, blockPos)) return false; @@ -44,6 +54,8 @@ public class DestoryBlockMoveBehavior extends MaidMoveToBlockTask { for (int dy = 0; dy < task.reachDistance(); dy = dy <= 0 ? 1 - dy : -dy) { for (int dz = 0; dz < task.reachDistance(); dz = dz <= 0 ? 1 - dz : -dz) { BlockPos pos = mb.offset(dx, dy, dz); + if (!Conditions.isGlobalValidTarget(entityMaid, pos, targetPos)) continue; + if (pos.distSqr(targetPos) > task.reachDistance() * task.reachDistance()) continue; if (entityMaid.isWithinRestriction(pos) && pathfindingBFS.canPathReach(pos)) { blockPosSet = task.toDestroyFromStanding(entityMaid, targetPos, pos); if (blockPosSet != null) { @@ -59,9 +71,17 @@ public class DestoryBlockMoveBehavior extends MaidMoveToBlockTask { return false; } + @Override protected @NotNull MaidPathFindingBFS getOrCreateArrivalMap(@NotNull ServerLevel worldIn, @NotNull EntityMaid maid) { - this.pathfindingBFS = super.getOrCreateArrivalMap(worldIn, maid); + if (this.pathfindingBFS == null) + this.pathfindingBFS = new MaidPathFindingBFS(maid.getNavigation().getNodeEvaluator(), worldIn, maid, 14); return this.pathfindingBFS; } + + @Override + protected void clearCurrentArrivalMap(MaidPathFindingBFS pathFinding) { + super.clearCurrentArrivalMap(pathFinding); + this.pathfindingBFS = null; + } } diff --git a/src/main/java/studio/fantasyit/maid_useful_task/behavior/LoopWithTokenBehavior.java b/src/main/java/studio/fantasyit/maid_useful_task/behavior/LoopWithTokenBehavior.java new file mode 100644 index 0000000..7c798c8 --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/behavior/LoopWithTokenBehavior.java @@ -0,0 +1,21 @@ +package studio.fantasyit.maid_useful_task.behavior; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.ai.behavior.Behavior; +import net.minecraft.world.entity.ai.memory.MemoryModuleType; +import net.minecraft.world.entity.ai.memory.MemoryStatus; +import studio.fantasyit.maid_useful_task.util.MemoryUtil; + +import java.util.Map; + +public class LoopWithTokenBehavior extends Behavior { + public LoopWithTokenBehavior() { + super(Map.of()); + } + + @Override + protected void start(ServerLevel p_22540_, EntityMaid p_22541_, long p_22542_) { + MemoryUtil.getRateLimitToken(p_22541_).tick(p_22541_); + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/behavior/MaidCenterMoveToBlockTask.java b/src/main/java/studio/fantasyit/maid_useful_task/behavior/MaidCenterMoveToBlockTask.java new file mode 100644 index 0000000..9734c96 --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/behavior/MaidCenterMoveToBlockTask.java @@ -0,0 +1,100 @@ +package studio.fantasyit.maid_useful_task.behavior; + + +import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.task.MaidCheckRateTask; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.MaidPathFindingBFS; +import com.github.tartaricacid.touhoulittlemaid.init.InitEntities; +import com.google.common.collect.ImmutableMap; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.ai.behavior.Behavior; +import net.minecraft.world.entity.ai.behavior.BehaviorUtils; +import net.minecraft.world.entity.ai.behavior.BlockPosTracker; +import net.minecraft.world.entity.ai.memory.MemoryModuleType; +import net.minecraft.world.entity.ai.memory.MemoryStatus; + +/** + * From https://github.com/TartaricAcid/TouhouLittleMaid + * 为了重载getWorkSearchPos而写的 + */ +abstract public class MaidCenterMoveToBlockTask extends Behavior { + + private static final int MAX_DELAY_TIME = 120; + private final float movementSpeed; + private final int verticalSearchRange; + private final int searchRange; + protected int verticalSearchStart; + + public MaidCenterMoveToBlockTask(float movementSpeed) { + this(movementSpeed, 1); + } + + public MaidCenterMoveToBlockTask(float movementSpeed, int verticalSearchRange) { + this(movementSpeed, verticalSearchRange, 7); + } + public MaidCenterMoveToBlockTask(float movementSpeed, int verticalSearchRange,int searchRange) { + super(ImmutableMap.of(MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT, InitEntities.TARGET_POS.get(), MemoryStatus.VALUE_ABSENT)); + this.movementSpeed = movementSpeed; + this.verticalSearchRange = verticalSearchRange; + this.searchRange = searchRange; + } + + protected final void searchForDestination(ServerLevel worldIn, EntityMaid maid) { + MaidPathFindingBFS pathFinding = this.getOrCreateArrivalMap(worldIn, maid); + BlockPos centrePos = this.getWorkSearchPos(maid); + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + + for(int y = this.verticalSearchStart; y <= this.verticalSearchRange; y = y > 0 ? -y : 1 - y) { + for(int i = 0; i < searchRange; ++i) { + for(int x = 0; x <= i; x = x > 0 ? -x : 1 - x) { + for(int z = x < i && x > -i ? i : 0; z <= i; z = z > 0 ? -z : 1 - z) { + mutableBlockPos.setWithOffset(centrePos, x, y - 1, z); + if (maid.isWithinRestriction(mutableBlockPos) && this.shouldMoveTo(worldIn, maid, mutableBlockPos) && this.checkPathReach(maid, pathFinding, mutableBlockPos) && this.checkOwnerPos(maid, mutableBlockPos)) { + BehaviorUtils.setWalkAndLookTargetMemories(maid, mutableBlockPos, this.movementSpeed, 0); + maid.getBrain().setMemory((MemoryModuleType)InitEntities.TARGET_POS.get(), new BlockPosTracker(mutableBlockPos)); + this.clearCurrentArrivalMap(pathFinding); + return; + } + } + } + } + } + + this.clearCurrentArrivalMap(pathFinding); + } + + protected void clearCurrentArrivalMap(MaidPathFindingBFS pathFinding) { + pathFinding.finish(); + } + + protected MaidPathFindingBFS getOrCreateArrivalMap(ServerLevel worldIn, EntityMaid maid) { + return new MaidPathFindingBFS(maid.getNavigation().getNodeEvaluator(), worldIn, maid); + } + + private BlockPos getWorkSearchPos(EntityMaid maid) { + return maid.blockPosition(); + } + + private boolean checkOwnerPos(EntityMaid maid, BlockPos mutableBlockPos) { + if (maid.isHomeModeEnable()) { + return true; + } else { + return maid.getOwner() != null && mutableBlockPos.closerToCenterThan(maid.getOwner().position(), 8.0); + } + } + + protected abstract boolean shouldMoveTo(ServerLevel var1, EntityMaid var2, BlockPos var3); + + /** @deprecated */ + @Deprecated( + forRemoval = true + ) + protected boolean checkPathReach(EntityMaid maid, BlockPos pos) { + return maid.canPathReach(pos); + } + + protected boolean checkPathReach(EntityMaid maid, MaidPathFindingBFS pathFinding, BlockPos pos) { + return pathFinding.canPathReach(pos); + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/behavior/PlaceBlockBehavior.java b/src/main/java/studio/fantasyit/maid_useful_task/behavior/PlaceBlockBehavior.java index 6a385c5..b356c57 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/behavior/PlaceBlockBehavior.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/behavior/PlaceBlockBehavior.java @@ -49,7 +49,7 @@ public class PlaceBlockBehavior extends Behavior { IMaidBlockPlaceTask task = (IMaidBlockPlaceTask) maid.getTask(); if (task.shouldPlacePos(maid, maid.getMainHandItem(), target)) { maid.swing(InteractionHand.MAIN_HAND); - task.tryPlaceBlock(maid, maid.getMainHandItem(), target); + task.tryPlaceBlock(maid, target); } target = null; } diff --git a/src/main/java/studio/fantasyit/maid_useful_task/behavior/PlaceBlockMoveBehavior.java b/src/main/java/studio/fantasyit/maid_useful_task/behavior/PlaceBlockMoveBehavior.java index fe018f5..4ec57f4 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/behavior/PlaceBlockMoveBehavior.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/behavior/PlaceBlockMoveBehavior.java @@ -10,13 +10,15 @@ import net.minecraft.world.item.ItemStack; import net.minecraftforge.items.wrapper.CombinedInvWrapper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import studio.fantasyit.maid_useful_task.memory.TaskRateLimitToken; import studio.fantasyit.maid_useful_task.task.IMaidBlockPlaceTask; +import studio.fantasyit.maid_useful_task.util.Conditions; import studio.fantasyit.maid_useful_task.util.MemoryUtil; import java.util.ArrayList; import java.util.List; -public class PlaceBlockMoveBehavior extends MaidMoveToBlockTask { +public class PlaceBlockMoveBehavior extends MaidCenterMoveToBlockTask { private IMaidBlockPlaceTask task; private MaidPathFindingBFS pathfindingBFS; private BlockPos targetPos; @@ -26,6 +28,15 @@ public class PlaceBlockMoveBehavior extends MaidMoveToBlockTask { super(0.5f, 4); } + + @Override + protected boolean checkExtraStartConditions(ServerLevel p_22538_, EntityMaid p_22539_) { + if (MemoryUtil.getRateLimitToken(p_22539_).isFor(TaskRateLimitToken.Level.L2)) { + return false; + } + return super.checkExtraStartConditions(p_22538_, p_22539_); + } + @Override protected void start(ServerLevel p_22540_, EntityMaid maid, long p_22542_) { super.start(p_22540_, maid, p_22542_); @@ -59,6 +70,7 @@ public class PlaceBlockMoveBehavior extends MaidMoveToBlockTask { for (int dy : dv) { for (int dz : dv) { BlockPos pos = mb.offset(dx, dy, dz); + if (!Conditions.isGlobalValidTarget(entityMaid, pos, targetPos)) continue; if (entityMaid.isWithinRestriction(pos) && pathfindingBFS.canPathReach(pos)) { mb.set(pos); return true; @@ -74,7 +86,14 @@ public class PlaceBlockMoveBehavior extends MaidMoveToBlockTask { @Override protected @NotNull MaidPathFindingBFS getOrCreateArrivalMap(@NotNull ServerLevel worldIn, @NotNull EntityMaid maid) { - this.pathfindingBFS = super.getOrCreateArrivalMap(worldIn, maid); + if (this.pathfindingBFS == null) + this.pathfindingBFS = new MaidPathFindingBFS(maid.getNavigation().getNodeEvaluator(), worldIn, maid, 10); return this.pathfindingBFS; } + + @Override + protected void clearCurrentArrivalMap(MaidPathFindingBFS pathFinding) { + super.clearCurrentArrivalMap(pathFinding); + this.pathfindingBFS = null; + } } \ No newline at end of file diff --git a/src/main/java/studio/fantasyit/maid_useful_task/memory/BlockUpContext.java b/src/main/java/studio/fantasyit/maid_useful_task/memory/BlockUpContext.java new file mode 100644 index 0000000..0c37bfa --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/memory/BlockUpContext.java @@ -0,0 +1,94 @@ +package studio.fantasyit.maid_useful_task.memory; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.BlockPos; + +import java.util.Optional; + +public class BlockUpContext { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + BlockPos.CODEC.optionalFieldOf("startPos").forGetter(BlockUpContext::getOptionalStartPos), + BlockPos.CODEC.optionalFieldOf("targetPos").forGetter(BlockUpContext::getOptionalTargetPos) + ) + .apply(instance, BlockUpContext::new) + ); + + public enum STATUS { + IDLE, + DOWN, + UP + } + + boolean isGoingDown = false; + /** + * 结束位置(女仆所在位置,实际方块为target-1) + */ + BlockPos targetPos; + BlockPos startPos; + + public BlockUpContext() { + this(Optional.empty(), Optional.empty()); + } + + public BlockUpContext(BlockPos startPos, BlockPos targetPos) { + this.startPos = startPos; + this.targetPos = targetPos; + } + + public BlockUpContext(Optional startPos, Optional targetPos) { + this.startPos = startPos.orElse(null); + this.targetPos = targetPos.orElse(null); + } + + public BlockPos getStartPos() { + return startPos; + } + + public BlockPos getTargetPos() { + return targetPos; + } + + public Optional getOptionalStartPos() { + return Optional.ofNullable(startPos); + } + + public Optional getOptionalTargetPos() { + return Optional.ofNullable(targetPos); + } + + + public boolean isOnLine(BlockPos pos) { + return pos.getX() == startPos.getX() && pos.getZ() == startPos.getZ() && pos.getY() >= startPos.getY() && pos.getY() <= targetPos.getY(); + } + + public void setStartTarget(BlockPos startPos, BlockPos targetPos) { + this.startPos = startPos; + this.targetPos = targetPos; + setStatus(STATUS.IDLE); + } + + public void clearStartTarget() { + this.startPos = null; + this.targetPos = null; + setStatus(STATUS.IDLE); + } + + public boolean hasTarget() { + return startPos != null && targetPos != null; + } + + public boolean isTarget(BlockPos pos) { + return pos.equals(targetPos); + } + + private STATUS status = STATUS.IDLE; + + public STATUS getStatus() { + return status; + } + + public void setStatus(STATUS status) { + this.status = status; + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/memory/TaskRateLimitToken.java b/src/main/java/studio/fantasyit/maid_useful_task/memory/TaskRateLimitToken.java new file mode 100644 index 0000000..dd87b2c --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/memory/TaskRateLimitToken.java @@ -0,0 +1,50 @@ +package studio.fantasyit.maid_useful_task.memory; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import studio.fantasyit.maid_useful_task.util.MemoryUtil; + +public class TaskRateLimitToken { + public enum Level { + + IDLE(0), + L1(1), + L2(2), + L3(3), + L4(4), + L5(5); + private final int level; + + Level(int level) { + this.level = level; + } + + public int getLevel() { + return level; + } + } + + int currentLevel = 0; + int cooldown = 100; + + public void tick(EntityMaid maid) { + if (MemoryUtil.getTargetPos(maid) != null) { + currentLevel = 0; + cooldown = 20; + return; + } + if (cooldown > 0) { + cooldown--; + return; + } + + currentLevel++; + if (currentLevel > 5) { + currentLevel = 0; + cooldown = 100; + } + } + + public boolean isFor(Level level) { + return currentLevel == level.getLevel(); + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidCheckPickupItem.java b/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidCheckPickupItem.java new file mode 100644 index 0000000..fbb8ffe --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidCheckPickupItem.java @@ -0,0 +1,19 @@ +package studio.fantasyit.maid_useful_task.mixin; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import net.minecraft.world.entity.item.ItemEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import studio.fantasyit.maid_useful_task.util.MemoryUtil; + +@Mixin(EntityMaid.class) +public abstract class MaidCheckPickupItem { + @Inject(method = "pickupItem", at = @At("HEAD"), cancellable = true, remap = false) + public void maid_storage_manager$pickupItem(ItemEntity entityItem, boolean simulate, CallbackInfoReturnable cir) { + if (MemoryUtil.getBlockUpContext((EntityMaid) (Object) this).hasTarget()) { + cir.setReturnValue(false); + } + } +} \ No newline at end of file diff --git a/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidMoveControlMixin.java b/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidMoveControlMixin.java new file mode 100644 index 0000000..a71f56c --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidMoveControlMixin.java @@ -0,0 +1,27 @@ +package studio.fantasyit.maid_useful_task.mixin; + +import com.github.tartaricacid.touhoulittlemaid.entity.ai.control.MaidMoveControl; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import studio.fantasyit.maid_useful_task.util.MemoryUtil; + +@Mixin(MaidMoveControl.class) +abstract public class MaidMoveControlMixin { + @Shadow(remap = false) + @Final + private EntityMaid maid; + + @Inject(method = "tick", at = @At("HEAD"), remap = false, cancellable = true) + public void tick(CallbackInfo ci) { + if (MemoryUtil.getBlockUpContext(this.maid).hasTarget()) { + if (MemoryUtil.getBlockUpContext(this.maid).isOnLine(this.maid.blockPosition())) { + ci.cancel(); + } + } + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidRunOneMixin.java b/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidRunOneMixin.java index 8fba877..1ab464e 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidRunOneMixin.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/mixin/MaidRunOneMixin.java @@ -11,9 +11,9 @@ import studio.fantasyit.maid_useful_task.util.MemoryUtil; @Mixin(MaidRunOne.class) abstract public class MaidRunOneMixin { - @Inject(method = "tryStart(Lnet/minecraft/server/level/ServerLevel;Lcom/github/tartaricacid/touhoulittlemaid/entity/passive/EntityMaid;J)Z", at = @At("HEAD"), cancellable = true,remap = false) + @Inject(method = "tryStart(Lnet/minecraft/server/level/ServerLevel;Lcom/github/tartaricacid/touhoulittlemaid/entity/passive/EntityMaid;J)Z", at = @At("HEAD"), cancellable = true, remap = false) public void runOne(ServerLevel pLevel, EntityMaid maid, long pGameTime, CallbackInfoReturnable cir) { - if (MemoryUtil.getDestroyTargetMemory(maid) != null || MemoryUtil.getPlaceTarget(maid) != null) + if (MemoryUtil.getDestroyTargetMemory(maid) != null || MemoryUtil.getPlaceTarget(maid) != null || MemoryUtil.getBlockUpContext(maid).hasTarget()) cir.setReturnValue(false); } } diff --git a/src/main/java/studio/fantasyit/maid_useful_task/registry/MemoryModuleRegistry.java b/src/main/java/studio/fantasyit/maid_useful_task/registry/MemoryModuleRegistry.java index 3af7be6..0e82fe7 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/registry/MemoryModuleRegistry.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/registry/MemoryModuleRegistry.java @@ -8,6 +8,8 @@ import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.RegistryObject; import studio.fantasyit.maid_useful_task.MaidUsefulTask; import studio.fantasyit.maid_useful_task.memory.BlockTargetMemory; +import studio.fantasyit.maid_useful_task.memory.BlockUpContext; +import studio.fantasyit.maid_useful_task.memory.TaskRateLimitToken; import java.util.Optional; @@ -18,6 +20,10 @@ public class MemoryModuleRegistry { = REGISTER.register("block_targets", () -> new MemoryModuleType<>(Optional.of(BlockTargetMemory.CODEC))); public static final RegistryObject> PLACE_TARGET = REGISTER.register("place_target", () -> new MemoryModuleType<>(Optional.empty())); + public static final RegistryObject> BLOCK_UP_TARGET + = REGISTER.register("block_up", () -> new MemoryModuleType<>(Optional.of(BlockUpContext.CODEC))); + public static final RegistryObject> RATE_LIMIT_TOKEN + = REGISTER.register("task_rate_limit", () -> new MemoryModuleType<>(Optional.empty())); public static void register(IEventBus eventBus) { REGISTER.register(eventBus); diff --git a/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockDestroyTask.java b/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockDestroyTask.java index c8069d4..467b8f2 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockDestroyTask.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockDestroyTask.java @@ -18,6 +18,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import studio.fantasyit.maid_useful_task.behavior.DestoryBlockBehavior; import studio.fantasyit.maid_useful_task.behavior.DestoryBlockMoveBehavior; +import studio.fantasyit.maid_useful_task.util.MaidUtils; import studio.fantasyit.maid_useful_task.util.WrappedMaidFakePlayer; import java.util.*; @@ -27,6 +28,9 @@ public interface IMaidBlockDestroyTask { List list = new ArrayList<>(); Vec3 eyePos = standPos.getCenter().add(0, maid.getEyeHeight() - 0.5, 0); Boolean available = BlockGetter.traverseBlocks(eyePos, targetPos.getCenter(), maid.level(), (level, pos) -> { + if (pos.distSqr(standPos) > reachDistance() * reachDistance()) { + return false; + } BlockState state = level.getBlockState(pos); if (state.isAir()) { return null; @@ -108,7 +112,8 @@ public interface IMaidBlockDestroyTask { * @return 是否可以破坏 */ default boolean canDestroyBlock(EntityMaid maid, BlockPos pos) { - if (maid.distanceToSqr(pos.getCenter()) > Math.pow(reachDistance(), 2)) { + //女仆可能少走一格,所以判断时给予补偿 + if (maid.distanceToSqr(pos.getCenter()) > Math.pow(reachDistance() + 1, 2)) { return false; } return true; @@ -117,32 +122,12 @@ public interface IMaidBlockDestroyTask { /** * 尝试破坏方块 * - * @param maid 女仆 - * @param blockPos 位置 + * @param maid 女仆 + * @param blockPos 位置 * @return */ default boolean tryDestroyBlock(EntityMaid maid, BlockPos blockPos) { - ServerLevel level = (ServerLevel) maid.level(); - BlockState blockState = level.getBlockState(blockPos); - if (blockState.isAir()) { - return false; - } else { - FluidState fluidState = level.getFluidState(blockPos); - if (!(blockState.getBlock() instanceof BaseFireBlock)) { - level.levelEvent(2001, blockPos, Block.getId(blockState)); - } - - BlockEntity blockEntity = blockState.hasBlockEntity() ? level.getBlockEntity(blockPos) : null; - //改用MainHandItem来roll loot - maid.dropResourcesToMaidInv(blockState, level, blockPos, blockEntity, maid, maid.getMainHandItem()); - - boolean setResult = level.setBlock(blockPos, fluidState.createLegacyBlock(), 3); - if (setResult) { - level.gameEvent(GameEvent.BLOCK_DESTROY, blockPos, GameEvent.Context.of(maid, blockState)); - } - - return setResult; - } + return MaidUtils.destroyBlock(maid, blockPos); } /** @@ -162,7 +147,7 @@ public interface IMaidBlockDestroyTask { } default int reachDistance() { - return 6; + return 7; } default @NotNull List>> createBrainTasks(@NotNull EntityMaid entityMaid) { diff --git a/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockPlaceTask.java b/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockPlaceTask.java index 75d207c..262d768 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockPlaceTask.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockPlaceTask.java @@ -14,6 +14,7 @@ import net.minecraft.world.phys.BlockHitResult; import org.jetbrains.annotations.NotNull; import studio.fantasyit.maid_useful_task.behavior.DestoryBlockBehavior; import studio.fantasyit.maid_useful_task.behavior.DestoryBlockMoveBehavior; +import studio.fantasyit.maid_useful_task.util.MaidUtils; import studio.fantasyit.maid_useful_task.util.WrappedMaidFakePlayer; import java.util.List; @@ -23,24 +24,8 @@ public interface IMaidBlockPlaceTask { boolean shouldPlacePos(EntityMaid maid, ItemStack itemStack, BlockPos pos); - default boolean tryPlaceBlock(EntityMaid maid, ItemStack itemStack, BlockPos pos){ - Player fakePlayer = WrappedMaidFakePlayer.get(maid); - BlockHitResult result = null; - ClipContext rayTraceContext = new ClipContext(maid.getPosition(0).add(0, maid.getEyeHeight(), 0), - pos.getCenter(), - ClipContext.Block.OUTLINE, - ClipContext.Fluid.NONE, - fakePlayer); - result = maid.level().clip(rayTraceContext); - UseOnContext useContext = new UseOnContext(fakePlayer, InteractionHand.MAIN_HAND, result); - InteractionResult actionresult = fakePlayer.getItemInHand(InteractionHand.MAIN_HAND).onItemUseFirst(useContext); - if (actionresult == InteractionResult.PASS) { - InteractionResult interactionResult = fakePlayer.getItemInHand(InteractionHand.MAIN_HAND).useOn(useContext); - if (interactionResult.consumesAction()) { - return true; - } - } - return false; + default boolean tryPlaceBlock(EntityMaid maid, BlockPos pos){ + return MaidUtils.placeBlock(maid,pos); } default @NotNull List>> createBrainTasks(@NotNull EntityMaid entityMaid) { return List.of( diff --git a/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockUpTask.java b/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockUpTask.java new file mode 100644 index 0000000..456e0ba --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/task/IMaidBlockUpTask.java @@ -0,0 +1,151 @@ +package studio.fantasyit.maid_useful_task.task; + +import com.github.tartaricacid.touhoulittlemaid.api.task.IMaidTask; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.MaidPathFindingBFS; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.wrapper.CombinedInvWrapper; +import oshi.util.tuples.Pair; +import studio.fantasyit.maid_useful_task.util.PosUtils; + +public interface IMaidBlockUpTask { + default boolean isFindingBlock(EntityMaid maid, BlockPos target, BlockPos standPos) { + if (target.distSqr(standPos) > touchLimit() * touchLimit()) + return false; + IMaidTask task = maid.getTask(); + if (task instanceof IMaidBlockDestroyTask destroyTask) { + return destroyTask.shouldDestroyBlock(maid, target); + } + return false; + } + + default boolean stillValid(EntityMaid maid, BlockPos startPos) { + for (int dx = 0; dx < touchLimit(); dx = dx <= 0 ? 1 - dx : -dx) { + for (int dz = 0; dz < touchLimit(); dz = dz <= 0 ? 1 - dz : -dz) { + for (int dy = 0; dy < verticalDistance(); dy++) { + BlockPos targetPos = startPos.offset(dx, dy, dz); + if (maid.hasRestriction() && !maid.isWithinRestriction(targetPos)) break; + if (isFindingBlock(maid, targetPos, startPos)) { + return true; + } + } + } + } + return false; + } + + default Pair findTargetPosBlockUp(EntityMaid maid, BlockPos center) { + ServerLevel level = (ServerLevel) maid.level(); + MaidPathFindingBFS pathFindingBFS = new MaidPathFindingBFS(maid.getNavigation().getNodeEvaluator(), level, maid); + for (int dx = 0; dx < scanRange(); dx = dx <= 0 ? 1 - dx : -dx) { + for (int dz = 0; dz < scanRange(); dz = dz <= 0 ? 1 - dz : -dz) { + BlockPos.MutableBlockPos mb = center.offset(dx, 0, dz).mutable(); + while (level.getBlockState(mb).canBeReplaced()) mb.move(0, -1, 0); + while (!level.getBlockState(mb).canBeReplaced()) mb.move(0, 1, 0); + if (!PosUtils.isFourSideAir(level, mb)) continue; + if (!pathFindingBFS.canPathReach(mb)) continue; + boolean valid = true; + for (int dy = 0; dy < verticalOffset(); dy++) { + BlockPos targetPos = center.offset(dx, dy, dz); + if (!level.getBlockState(targetPos).canBeReplaced()) { + valid = false; + break; + } + } + for (int dy = verticalDistance(); dy < verticalDistance() + 2; dy++) { + BlockPos targetPos = center.offset(dx, dy, dz); + if (!level.getBlockState(targetPos).isAir()) { + valid = false; + break; + } + } + if (!valid) + continue; + int touchLimit = touchLimit() + 1; + boolean continuous = true; + BlockPos standPos = center.offset(dx, verticalOffset(), dz); + for (int dy = verticalOffset(); dy < verticalDistance() + verticalOffset(); dy++) { + BlockPos targetPos = center.offset(dx, dy, dz); + if (maid.hasRestriction() && !maid.isWithinRestriction(targetPos)) break; + if (isFindingBlock(maid, targetPos, standPos)) { + return new Pair<>(mb, standPos); + } + //头顶一格是不是空气,不是:不连续空间(不能继续垫了,那么需要计算触及范围 + if (!level.getBlockState(standPos.above().above()).isAir()) { + continuous = false; + } + + if (!continuous) { + touchLimit--; + } else { + standPos = standPos.above(); + } + if (touchLimit <= 0) + break; + } + } + } + return null; + } + + default int countMaxUsableBlockItems(EntityMaid maid) { + CombinedInvWrapper inv = maid.getAvailableInv(true); + int count = 0; + for (int i = 0; i < inv.getSlots(); i++) { + ItemStack stack = inv.getStackInSlot(i); + if (isValidItemStack(maid, stack)) { + count++; + } + } + return count; + } + + default boolean swapValidItemToHand(EntityMaid maid) { + CombinedInvWrapper inv = maid.getAvailableInv(true); + for (int i = 0; i < inv.getSlots(); i++) { + ItemStack stack = inv.getStackInSlot(i); + if (isValidItemStack(maid, stack)) { + inv.setStackInSlot(i, maid.getMainHandItem()); + maid.setItemInHand(InteractionHand.MAIN_HAND, stack); + return true; + } + } + return false; + } + + default boolean swapValidToolToHand(EntityMaid maid) { + CombinedInvWrapper inv = maid.getAvailableInv(true); + for (int i = 0; i < inv.getSlots(); i++) { + ItemStack stack = inv.getStackInSlot(i); + if (isDestroyTool(maid, stack)) { + inv.setStackInSlot(i, maid.getMainHandItem()); + maid.setItemInHand(InteractionHand.MAIN_HAND, stack); + return true; + } + } + return false; + } + + boolean isValidItemStack(EntityMaid maid, ItemStack stack); + + boolean isDestroyTool(EntityMaid maid, ItemStack stack); + + default int verticalOffset() { + return 2; + } + + default int verticalDistance() { + return 15; + } + + default int scanRange() { + return 10; + } + + default int touchLimit() { + return 7; + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/task/MaidTreeTask.java b/src/main/java/studio/fantasyit/maid_useful_task/task/MaidTreeTask.java index 3d5f01a..c0cd289 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/task/MaidTreeTask.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/task/MaidTreeTask.java @@ -19,16 +19,13 @@ import net.minecraftforge.items.wrapper.CombinedInvWrapper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import studio.fantasyit.maid_useful_task.MaidUsefulTask; -import studio.fantasyit.maid_useful_task.behavior.DestoryBlockBehavior; -import studio.fantasyit.maid_useful_task.behavior.DestoryBlockMoveBehavior; -import studio.fantasyit.maid_useful_task.behavior.PlaceBlockBehavior; -import studio.fantasyit.maid_useful_task.behavior.PlaceBlockMoveBehavior; +import studio.fantasyit.maid_useful_task.behavior.*; import studio.fantasyit.maid_useful_task.util.WrappedMaidFakePlayer; import java.util.ArrayList; import java.util.List; -public class MaidTreeTask implements IMaidTask, IMaidBlockPlaceTask, IMaidBlockDestroyTask { +public class MaidTreeTask implements IMaidTask, IMaidBlockPlaceTask, IMaidBlockDestroyTask, IMaidBlockUpTask { @Override public ResourceLocation getUid() { return new ResourceLocation(MaidUsefulTask.MODID, "maid_tree"); @@ -73,7 +70,7 @@ public class MaidTreeTask implements IMaidTask, IMaidBlockPlaceTask, IMaidBlockD @Override public boolean shouldPlacePos(EntityMaid maid, ItemStack itemStack, BlockPos pos) { ServerLevel level = (ServerLevel) maid.level(); - if(!level.getBlockState(pos.below()).is(BlockTags.DIRT)) return false; + if (!level.getBlockState(pos.below()).is(BlockTags.DIRT)) return false; if (!level.getBlockState(pos).canBeReplaced()) return false; final int[] dv = {0, 1, -1, 2, -2}; for (int dx : dv) { @@ -154,12 +151,34 @@ public class MaidTreeTask implements IMaidTask, IMaidBlockPlaceTask, IMaidBlockD @Override public List>> createBrainTasks(EntityMaid entityMaid) { ArrayList>> list = new ArrayList<>(); + list.add(Pair.of(6, new LoopWithTokenBehavior())); list.add(Pair.of(5, new DestoryBlockBehavior())); list.add(Pair.of(4, new DestoryBlockMoveBehavior())); list.add(Pair.of(3, new PlaceBlockBehavior())); list.add(Pair.of(2, new PlaceBlockMoveBehavior())); + list.add(Pair.of(1, new BlockUpScheduleBehavior())); + list.add(Pair.of(0, new BlockUpPlaceBehavior())); + list.add(Pair.of(0, new BlockUpDestroyBehavior())); + return list; } + + @Override + public boolean isValidItemStack(EntityMaid maid, ItemStack stack) { + return stack.is(ItemTags.LOGS); + } + + @Override + public boolean isDestroyTool(EntityMaid maid, ItemStack stack) { + return stack.is(ItemTags.AXES); + } + + @Override + public boolean isFindingBlock(EntityMaid maid, BlockPos target, BlockPos standPos) { + if (target.distSqr(standPos) > touchLimit() * touchLimit()) + return false; + return maid.level().getBlockState(target).is(BlockTags.LOGS) && isValidNatureTree(maid, target); + } } diff --git a/src/main/java/studio/fantasyit/maid_useful_task/util/Conditions.java b/src/main/java/studio/fantasyit/maid_useful_task/util/Conditions.java index 58ffeb8..d8f9eee 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/util/Conditions.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/util/Conditions.java @@ -2,17 +2,20 @@ package studio.fantasyit.maid_useful_task.util; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.init.InitEntities; +import net.minecraft.core.BlockPos; import net.minecraft.world.entity.ai.Brain; import net.minecraft.world.entity.ai.memory.MemoryModuleType; import net.minecraft.world.entity.ai.memory.WalkTarget; import net.minecraft.world.phys.Vec3; +import studio.fantasyit.maid_useful_task.registry.MemoryModuleRegistry; import java.util.Optional; public class Conditions { - public static boolean hasReachedValidTargetOrReset(EntityMaid maid){ + public static boolean hasReachedValidTargetOrReset(EntityMaid maid) { return hasReachedValidTargetOrReset(maid, 2); } + public static boolean hasReachedValidTargetOrReset(EntityMaid maid, float closeEnough) { Brain brain = maid.getBrain(); return brain.getMemory(InitEntities.TARGET_POS.get()).map(targetPos -> { @@ -35,4 +38,13 @@ public class Conditions { } return maid.getDeltaMovement().length() < 0.2; } + + public static boolean isGlobalValidTarget(EntityMaid maid, BlockPos pos, BlockPos targetPos) { + if (MemoryUtil.getBlockUpContext(maid).hasTarget()) { + if (targetPos.getY() < maid.getBlockY() && targetPos.getX() == maid.getBlockX() && targetPos.getZ() == maid.getBlockZ()) + return false; + return MemoryUtil.getBlockUpContext(maid).isTarget(pos); + } + return true; + } } diff --git a/src/main/java/studio/fantasyit/maid_useful_task/util/MaidUtils.java b/src/main/java/studio/fantasyit/maid_useful_task/util/MaidUtils.java index f08c2dc..ffc07e8 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/util/MaidUtils.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/util/MaidUtils.java @@ -2,9 +2,21 @@ package studio.fantasyit.maid_useful_task.util; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.mojang.authlib.GameProfile; +import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.block.BaseFireBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.phys.BlockHitResult; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.common.util.FakePlayerFactory; import net.minecraftforge.items.wrapper.RangedWrapper; @@ -25,4 +37,47 @@ public class MaidUtils { } } } + public static boolean destroyBlock(EntityMaid maid, BlockPos blockPos){ + ServerLevel level = (ServerLevel) maid.level(); + BlockState blockState = level.getBlockState(blockPos); + if (blockState.isAir()) { + return false; + } else { + FluidState fluidState = level.getFluidState(blockPos); + if (!(blockState.getBlock() instanceof BaseFireBlock)) { + level.levelEvent(2001, blockPos, Block.getId(blockState)); + } + + BlockEntity blockEntity = blockState.hasBlockEntity() ? level.getBlockEntity(blockPos) : null; + //改用MainHandItem来roll loot + maid.dropResourcesToMaidInv(blockState, level, blockPos, blockEntity, maid, maid.getMainHandItem()); + + boolean setResult = level.setBlock(blockPos, fluidState.createLegacyBlock(), 3); + if (setResult) { + level.gameEvent(GameEvent.BLOCK_DESTROY, blockPos, GameEvent.Context.of(maid, blockState)); + } + + return setResult; + } + } + + public static boolean placeBlock(EntityMaid maid, BlockPos pos) { + Player fakePlayer = WrappedMaidFakePlayer.get(maid); + BlockHitResult result = null; + ClipContext rayTraceContext = new ClipContext(maid.getPosition(0).add(0, maid.getEyeHeight(), 0), + pos.getCenter(), + ClipContext.Block.OUTLINE, + ClipContext.Fluid.NONE, + fakePlayer); + result = maid.level().clip(rayTraceContext); + UseOnContext useContext = new UseOnContext(fakePlayer, InteractionHand.MAIN_HAND, result); + InteractionResult actionresult = fakePlayer.getMainHandItem().onItemUseFirst(useContext); + if (actionresult == InteractionResult.PASS) { + InteractionResult interactionResult = fakePlayer.getMainHandItem().useOn(useContext); + if (interactionResult.consumesAction()) { + return true; + } + } + return false; + } } diff --git a/src/main/java/studio/fantasyit/maid_useful_task/util/MemoryUtil.java b/src/main/java/studio/fantasyit/maid_useful_task/util/MemoryUtil.java index a7b106a..2c24ee0 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/util/MemoryUtil.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/util/MemoryUtil.java @@ -3,11 +3,15 @@ package studio.fantasyit.maid_useful_task.util; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.init.InitEntities; import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.ai.Brain; import net.minecraft.world.entity.ai.behavior.BlockPosTracker; import net.minecraft.world.entity.ai.behavior.PositionTracker; import net.minecraft.world.entity.ai.memory.MemoryModuleType; import org.jetbrains.annotations.Nullable; import studio.fantasyit.maid_useful_task.memory.BlockTargetMemory; +import studio.fantasyit.maid_useful_task.memory.BlockUpContext; +import studio.fantasyit.maid_useful_task.memory.TaskRateLimitToken; import studio.fantasyit.maid_useful_task.registry.MemoryModuleRegistry; import java.util.List; @@ -50,4 +54,20 @@ public class MemoryUtil { public static void setLookAt(EntityMaid maid, BlockPos pos) { maid.getBrain().setMemory(MemoryModuleType.LOOK_TARGET, new BlockPosTracker(pos)); } + + public static BlockUpContext getBlockUpContext(EntityMaid maid){ + Brain brain = maid.getBrain(); + if(!brain.hasMemoryValue(MemoryModuleRegistry.BLOCK_UP_TARGET.get())){ + brain.setMemory(MemoryModuleRegistry.BLOCK_UP_TARGET.get(), new BlockUpContext()); + } + return brain.getMemory(MemoryModuleRegistry.BLOCK_UP_TARGET.get()).get(); + } + public static TaskRateLimitToken getRateLimitToken(EntityMaid maid){ + Brain brain = maid.getBrain(); + if(!brain.hasMemoryValue(MemoryModuleRegistry.RATE_LIMIT_TOKEN.get())){ + brain.setMemory(MemoryModuleRegistry.RATE_LIMIT_TOKEN.get(), new TaskRateLimitToken()); + } + return brain.getMemory(MemoryModuleRegistry.RATE_LIMIT_TOKEN.get()).get(); + } + } diff --git a/src/main/java/studio/fantasyit/maid_useful_task/util/PosUtils.java b/src/main/java/studio/fantasyit/maid_useful_task/util/PosUtils.java new file mode 100644 index 0000000..2d2d02e --- /dev/null +++ b/src/main/java/studio/fantasyit/maid_useful_task/util/PosUtils.java @@ -0,0 +1,14 @@ +package studio.fantasyit.maid_useful_task.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; + +public class PosUtils { + public static boolean isFourSideAir(BlockGetter level, BlockPos pos) { + return level.getBlockState(pos).isAir() && + level.getBlockState(pos.north()).isAir() && + level.getBlockState(pos.south()).isAir() && + level.getBlockState(pos.east()).isAir() && + level.getBlockState(pos.west()).isAir(); + } +} diff --git a/src/main/java/studio/fantasyit/maid_useful_task/util/WrappedMaidFakePlayer.java b/src/main/java/studio/fantasyit/maid_useful_task/util/WrappedMaidFakePlayer.java index e9e4724..4c5f9bd 100644 --- a/src/main/java/studio/fantasyit/maid_useful_task/util/WrappedMaidFakePlayer.java +++ b/src/main/java/studio/fantasyit/maid_useful_task/util/WrappedMaidFakePlayer.java @@ -34,12 +34,17 @@ public class WrappedMaidFakePlayer extends FakePlayer { public static WrappedMaidFakePlayer get(EntityMaid maid) { if (cache.containsKey(maid.getUUID())) { - return cache.get(maid.getUUID()); - } else { - WrappedMaidFakePlayer fakePlayer = new WrappedMaidFakePlayer(maid); - cache.put(maid.getUUID(), fakePlayer); - return fakePlayer; + WrappedMaidFakePlayer wrappedMaidFakePlayer = cache.get(maid.getUUID()); + if (!wrappedMaidFakePlayer.maid.isAlive()) { + cache.remove(maid.getUUID()); + } else { + return wrappedMaidFakePlayer; + } } + WrappedMaidFakePlayer fakePlayer = new WrappedMaidFakePlayer(maid); + cache.put(maid.getUUID(), fakePlayer); + return fakePlayer; + } private WrappedMaidFakePlayer(EntityMaid maid) { @@ -49,27 +54,32 @@ public class WrappedMaidFakePlayer extends FakePlayer { @Override public boolean removeEffect(MobEffect p_21196_) { + if (maid == null) return false; return maid.removeEffect(p_21196_); } @Nullable @Override public MobEffectInstance removeEffectNoUpdate(@Nullable MobEffect p_21164_) { + if (maid == null) return super.removeEffectNoUpdate(p_21164_); return maid.removeEffectNoUpdate(p_21164_); } @Override public boolean removeAllEffects() { + if (maid == null) return false; return maid.removeAllEffects(); } @Override public boolean addEffect(MobEffectInstance p_147208_, @Nullable Entity p_147209_) { + if (maid == null) return super.addEffect(p_147208_, p_147209_); return maid.addEffect(p_147208_, p_147209_); } @Override public boolean canBeAffected(MobEffectInstance p_21197_) { + if (maid == null) return super.canBeAffected(p_21197_); return maid.canBeAffected(p_21197_); } @@ -81,21 +91,25 @@ public class WrappedMaidFakePlayer extends FakePlayer { @Nullable @Override public MobEffectInstance getEffect(MobEffect p_21125_) { + if (maid == null) return super.getEffect(p_21125_); return maid.getEffect(p_21125_); } @Override public Collection getActiveEffects() { + if (maid == null) return super.getActiveEffects(); return maid.getActiveEffects(); } @Override public Map getActiveEffectsMap() { + if (maid == null) return super.getActiveEffectsMap(); return maid.getActiveEffectsMap(); } @Override public boolean hasEffect(MobEffect p_21024_) { + if (maid == null) return super.hasEffect(p_21024_); return maid.hasEffect(p_21024_); } @@ -105,13 +119,21 @@ public class WrappedMaidFakePlayer extends FakePlayer { return maid.getMainHandItem(); } + @Override + public ItemStack getItemInHand(InteractionHand p_21121_) { + if (maid == null) return super.getItemInHand(p_21121_); + return maid.getItemInHand(p_21121_); + } + @Override public void setItemInHand(InteractionHand p_21009_, ItemStack p_21010_) { + if (maid == null) return; maid.setItemInHand(p_21009_, p_21010_); } @Override public void setItemSlot(EquipmentSlot p_36161_, ItemStack p_36162_) { + if (maid == null) return; maid.setItemSlot(p_36161_, p_36162_); } @@ -123,11 +145,13 @@ public class WrappedMaidFakePlayer extends FakePlayer { @Override public boolean isEyeInFluid(TagKey p_204030_) { + if (maid == null) return false; return maid.isEyeInFluid(p_204030_); } @Override public boolean onGround() { + if (maid == null) return false; return maid.onGround(); } @@ -169,6 +193,7 @@ public class WrappedMaidFakePlayer extends FakePlayer { @Override public void teleportRelative(double p_251611_, double p_248861_, double p_252266_) { + if (maid == null) return; maid.teleportRelative(p_251611_, p_248861_, p_252266_); } @@ -180,6 +205,7 @@ public class WrappedMaidFakePlayer extends FakePlayer { @Override public ChunkPos chunkPosition() { + if (maid == null) return new ChunkPos(0, 0); return maid.chunkPosition(); } } diff --git a/src/main/resources/maid_useful_task.mixins.json b/src/main/resources/maid_useful_task.mixins.json index 0d1615c..91c8945 100644 --- a/src/main/resources/maid_useful_task.mixins.json +++ b/src/main/resources/maid_useful_task.mixins.json @@ -5,6 +5,8 @@ "compatibilityLevel": "JAVA_8", "refmap": "maid_useful_task.refmap.json", "mixins": [ + "MaidCheckPickupItem", + "MaidMoveControlMixin", "MaidRunOneMixin" ], "client": [