支持blockup寻路

This commit is contained in:
xypp
2025-04-26 21:02:00 +08:00
parent 7a2e7e4954
commit 0aa91679e3
28 changed files with 946 additions and 67 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -26,7 +26,9 @@ public class UsefulTaskExtension implements ILittleMaid {
public List<MemoryModuleType<?>> 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()
);
}
});

View File

@@ -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<EntityMaid> {
private BlockUpContext context;
private IMaidBlockUpTask task;
WrappedMaidFakePlayer fakePlayer;
public BlockUpDestroyBehavior(Map<MemoryModuleType<?>, 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);
}
}

View File

@@ -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<EntityMaid> {
private BlockUpContext context;
private IMaidBlockUpTask task;
private int tickCount;
public BlockUpPlaceBehavior(Map<MemoryModuleType<?>, 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);
}
}

View File

@@ -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<EntityMaid> {
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<BlockPos, BlockPos> 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);
}
}
}
}

View File

@@ -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<EntityMaid> {
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;

View File

@@ -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<BlockPos> 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;
}
}

View File

@@ -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<EntityMaid> {
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_);
}
}

View File

@@ -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<EntityMaid> {
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);
}
}

View File

@@ -49,7 +49,7 @@ public class PlaceBlockBehavior extends Behavior<EntityMaid> {
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;
}

View File

@@ -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;
}
}

View File

@@ -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<BlockUpContext> 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<BlockPos> startPos, Optional<BlockPos> targetPos) {
this.startPos = startPos.orElse(null);
this.targetPos = targetPos.orElse(null);
}
public BlockPos getStartPos() {
return startPos;
}
public BlockPos getTargetPos() {
return targetPos;
}
public Optional<BlockPos> getOptionalStartPos() {
return Optional.ofNullable(startPos);
}
public Optional<BlockPos> 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;
}
}

View File

@@ -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();
}
}

View File

@@ -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<Boolean> cir) {
if (MemoryUtil.getBlockUpContext((EntityMaid) (Object) this).hasTarget()) {
cir.setReturnValue(false);
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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<Boolean> 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);
}
}

View File

@@ -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<MemoryModuleType<BlockPos>> PLACE_TARGET
= REGISTER.register("place_target", () -> new MemoryModuleType<>(Optional.empty()));
public static final RegistryObject<MemoryModuleType<BlockUpContext>> BLOCK_UP_TARGET
= REGISTER.register("block_up", () -> new MemoryModuleType<>(Optional.of(BlockUpContext.CODEC)));
public static final RegistryObject<MemoryModuleType<TaskRateLimitToken>> RATE_LIMIT_TOKEN
= REGISTER.register("task_rate_limit", () -> new MemoryModuleType<>(Optional.empty()));
public static void register(IEventBus eventBus) {
REGISTER.register(eventBus);

View File

@@ -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<BlockPos> 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<Pair<Integer, BehaviorControl<? super EntityMaid>>> createBrainTasks(@NotNull EntityMaid entityMaid) {

View File

@@ -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<Pair<Integer, BehaviorControl<? super EntityMaid>>> createBrainTasks(@NotNull EntityMaid entityMaid) {
return List.of(

View File

@@ -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<BlockPos, BlockPos> 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;
}
}

View File

@@ -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<Pair<Integer, BehaviorControl<? super EntityMaid>>> createBrainTasks(EntityMaid entityMaid) {
ArrayList<Pair<Integer, BehaviorControl<? super EntityMaid>>> 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);
}
}

View File

@@ -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<EntityMaid> 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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<EntityMaid> 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<EntityMaid> 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();
}
}

View File

@@ -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();
}
}

View File

@@ -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<MobEffectInstance> getActiveEffects() {
if (maid == null) return super.getActiveEffects();
return maid.getActiveEffects();
}
@Override
public Map<MobEffect, MobEffectInstance> 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<Fluid> 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();
}
}

View File

@@ -5,6 +5,8 @@
"compatibilityLevel": "JAVA_8",
"refmap": "maid_useful_task.refmap.json",
"mixins": [
"MaidCheckPickupItem",
"MaidMoveControlMixin",
"MaidRunOneMixin"
],
"client": [