Skip to content

Commit

Permalink
应用女仆对栅栏门的交互 (#567)
Browse files Browse the repository at this point in the history
* 完成栅栏门的交互

* 应用门的交互(记忆)机制到栅栏门上

* 简化栅栏门的设计

---------

Co-authored-by: tartaric_acid <[email protected]>
  • Loading branch information
Azumic and TartaricAcid committed Sep 8, 2024
1 parent 1bb3bb4 commit e3691fa
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
import net.minecraft.core.GlobalPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.Brain;
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.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.DoorBlock;
import net.minecraft.world.level.block.FenceGateBlock;
import net.minecraft.world.level.block.LevelEvent;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.pathfinder.Node;
import net.minecraft.world.level.pathfinder.Path;

Expand All @@ -33,22 +39,79 @@ public MaidInteractWithDoor() {
MemoryModuleType.DOORS_TO_CLOSE, MemoryStatus.REGISTERED));
}

public static void closeDoorsThatIHaveOpenedOrPassedThrough(ServerLevel serverWorld, LivingEntity entity, @Nullable Node pathPoint, @Nullable Node pathPoint1) {
@Override
protected boolean checkExtraStartConditions(ServerLevel serverWorld, LivingEntity entity) {
Path path = entity.getBrain().getMemory(MemoryModuleType.PATH).get();
if (!path.notStarted() && !path.isDone()) {
if (!Objects.equals(this.lastCheckedNode, path.getNextNode())) {
this.remainingCooldown = COOLDOWN_BEFORE_RERUNNING_IN_SAME_NODE;
return true;
} else {
if (this.remainingCooldown > 0) {
--this.remainingCooldown;
}
return this.remainingCooldown == 0;
}
} else {
return false;
}
}

@Override
protected void start(ServerLevel serverWorld, LivingEntity entity, long gameTime) {
Path path = entity.getBrain().getMemory(MemoryModuleType.PATH).get();
this.lastCheckedNode = path.getNextNode();
Node previousNode = path.getPreviousNode();
Node nextNode = path.getNextNode();

BlockPos previousPos = previousNode.asBlockPos();
BlockState previousBlockState = serverWorld.getBlockState(previousPos);
if (previousBlockState.is(BlockTags.WOODEN_DOORS, (stateBase) -> stateBase.getBlock() instanceof DoorBlock)) {
DoorBlock doorblock = (DoorBlock) previousBlockState.getBlock();
if (!doorblock.isOpen(previousBlockState)) {
doorblock.setOpen(entity, serverWorld, previousBlockState, previousPos, true);
}
this.rememberDoorToClose(serverWorld, entity, previousPos);
} else if (previousBlockState.is(BlockTags.FENCE_GATES, (stateBase) -> stateBase.getBlock() instanceof FenceGateBlock)) {
if (!previousBlockState.getValue(FenceGateBlock.OPEN)) {
setFenceGate(entity, serverWorld, previousBlockState, previousPos, true);
}
this.rememberDoorToClose(serverWorld, entity, previousPos);
}


BlockPos nextPos = nextNode.asBlockPos();
BlockState nextBlockState = serverWorld.getBlockState(nextPos);
if (nextBlockState.is(BlockTags.WOODEN_DOORS, (stateBase) -> stateBase.getBlock() instanceof DoorBlock)) {
DoorBlock doorBlock = (DoorBlock) nextBlockState.getBlock();
if (!doorBlock.isOpen(nextBlockState)) {
doorBlock.setOpen(entity, serverWorld, nextBlockState, nextPos, true);
this.rememberDoorToClose(serverWorld, entity, nextPos);
}
} else if (nextBlockState.is(BlockTags.FENCE_GATES, (stateBase) -> stateBase.getBlock() instanceof FenceGateBlock)) {
if (!nextBlockState.getValue(FenceGateBlock.OPEN)) {
setFenceGate(entity, serverWorld, nextBlockState, nextPos, true);
this.rememberDoorToClose(serverWorld, entity, nextPos);
}
}

closeDoorsThatIHaveOpenedOrPassedThrough(serverWorld, entity, previousNode, nextNode);
}

public static void closeDoorsThatIHaveOpenedOrPassedThrough(ServerLevel serverWorld, LivingEntity entity, @Nullable Node previousNode, @Nullable Node nextNode) {
Brain<?> brain = entity.getBrain();
if (brain.hasMemoryValue(MemoryModuleType.DOORS_TO_CLOSE)) {
Iterator<GlobalPos> iterator = brain.getMemory(MemoryModuleType.DOORS_TO_CLOSE).get().iterator();
while (iterator.hasNext()) {
GlobalPos globalpos = iterator.next();
BlockPos blockpos = globalpos.pos();
if ((pathPoint == null || !pathPoint.asBlockPos().equals(blockpos) || (pathPoint1 != null && entity.blockPosition().equals(pathPoint1.asBlockPos())))
&& (pathPoint1 == null || !pathPoint1.asBlockPos().equals(blockpos))) {
if ((previousNode == null || !previousNode.asBlockPos().equals(blockpos) || (nextNode != null && entity.blockPosition().equals(nextNode.asBlockPos())))
&& (nextNode == null || !nextNode.asBlockPos().equals(blockpos))) {
if (isDoorTooFarAway(serverWorld, entity, globalpos)) {
iterator.remove();
} else {
BlockState blockstate = serverWorld.getBlockState(blockpos);
if (!blockstate.is(BlockTags.WOODEN_DOORS)) {
iterator.remove();
} else {
if (blockstate.is(BlockTags.WOODEN_DOORS, (stateBase) -> stateBase.getBlock() instanceof DoorBlock)) {
DoorBlock doorblock = (DoorBlock) blockstate.getBlock();
if (!doorblock.isOpen(blockstate)) {
iterator.remove();
Expand All @@ -58,6 +121,16 @@ public static void closeDoorsThatIHaveOpenedOrPassedThrough(ServerLevel serverWo
doorblock.setOpen(entity, serverWorld, blockstate, blockpos, false);
iterator.remove();
}
} else if ((blockstate.is(BlockTags.FENCE_GATES, (stateBase) -> stateBase.getBlock() instanceof FenceGateBlock))) {
if (!blockstate.getValue(FenceGateBlock.OPEN)) {
iterator.remove();
} else if (areOtherMobsComingThroughDoor(serverWorld, entity, blockpos)) {
iterator.remove();
} else {
setFenceGate(entity, serverWorld, blockstate, blockpos, false);
}
} else {
iterator.remove();
}
}
}
Expand All @@ -81,12 +154,12 @@ private static boolean isMobComingThroughDoor(ServerLevel serverWorld, LivingEnt
if (path.isDone()) {
return false;
} else {
Node pathpoint = path.getPreviousNode();
if (pathpoint == null) {
Node previousNode = path.getPreviousNode();
if (previousNode == null) {
return false;
} else {
Node nextNode = path.getNextNode();
return blockPos.equals(pathpoint.asBlockPos()) || blockPos.equals(nextNode.asBlockPos());
return blockPos.equals(previousNode.asBlockPos()) || blockPos.equals(nextNode.asBlockPos());
}
}
}
Expand All @@ -96,51 +169,6 @@ private static boolean isDoorTooFarAway(ServerLevel serverWorld, LivingEntity en
return globalPos.dimension() != serverWorld.dimension() || !globalPos.pos().closerToCenterThan(entity.position(), SKIP_CLOSING_DOOR_IF_FURTHER_AWAY_THAN);
}

@Override
protected boolean checkExtraStartConditions(ServerLevel serverWorld, LivingEntity entity) {
Path path = entity.getBrain().getMemory(MemoryModuleType.PATH).get();
if (!path.notStarted() && !path.isDone()) {
if (!Objects.equals(this.lastCheckedNode, path.getNextNode())) {
this.remainingCooldown = COOLDOWN_BEFORE_RERUNNING_IN_SAME_NODE;
return true;
} else {
if (this.remainingCooldown > 0) {
--this.remainingCooldown;
}
return this.remainingCooldown == 0;
}
} else {
return false;
}
}

@Override
protected void start(ServerLevel serverWorld, LivingEntity entity, long gameTime) {
Path path = entity.getBrain().getMemory(MemoryModuleType.PATH).get();
this.lastCheckedNode = path.getNextNode();
Node previousNode = path.getPreviousNode();
Node nextNode = path.getNextNode();
BlockPos blockpos = previousNode.asBlockPos();
BlockState blockstate = serverWorld.getBlockState(blockpos);
if (blockstate.is(BlockTags.WOODEN_DOORS)) {
DoorBlock doorblock = (DoorBlock) blockstate.getBlock();
if (!doorblock.isOpen(blockstate)) {
doorblock.setOpen(entity, serverWorld, blockstate, blockpos, true);
}
this.rememberDoorToClose(serverWorld, entity, blockpos);
}
BlockPos blockPos = nextNode.asBlockPos();
BlockState blockState = serverWorld.getBlockState(blockPos);
if (blockState.is(BlockTags.WOODEN_DOORS)) {
DoorBlock doorBlock = (DoorBlock) blockState.getBlock();
if (!doorBlock.isOpen(blockState)) {
doorBlock.setOpen(entity, serverWorld, blockState, blockPos, true);
this.rememberDoorToClose(serverWorld, entity, blockPos);
}
}
closeDoorsThatIHaveOpenedOrPassedThrough(serverWorld, entity, previousNode, nextNode);
}

private void rememberDoorToClose(ServerLevel serverWorld, LivingEntity entity, BlockPos blockPos) {
Brain<?> brain = entity.getBrain();
GlobalPos globalpos = GlobalPos.of(serverWorld.dimension(), blockPos);
Expand All @@ -150,4 +178,10 @@ private void rememberDoorToClose(ServerLevel serverWorld, LivingEntity entity, B
brain.setMemory(MemoryModuleType.DOORS_TO_CLOSE, Sets.newHashSet(globalpos));
}
}

private static void setFenceGate(@Nullable Entity entity, Level serverLevel, BlockState blockstate, BlockPos blockPos, boolean isOpen) {
serverLevel.setBlock(blockPos, blockstate.setValue(FenceGateBlock.OPEN, isOpen), Block.UPDATE_CLIENTS | Block.UPDATE_IMMEDIATE);
serverLevel.levelEvent(null, isOpen ? LevelEvent.SOUND_OPEN_FENCE_GATE : LevelEvent.SOUND_CLOSE_FENCE_GATE, blockPos, 0);
serverLevel.gameEvent(entity, isOpen ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, blockPos);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.github.tartaricacid.touhoulittlemaid.entity.ai.navigation;

import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.FenceGateBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;

/**
* 该方法仅修改了栅栏门的寻路判断
*/
public class MaidNodeEvaluator extends WalkNodeEvaluator {
@Override
public BlockPathTypes getBlockPathType(BlockGetter level, int pX, int pY, int pZ) {
return getMaidBlockPathTypeStatic(level, new BlockPos.MutableBlockPos(pX, pY, pZ));
}

private static BlockPathTypes getMaidBlockPathTypeStatic(BlockGetter level, BlockPos.MutableBlockPos pos) {
int x = pos.getX();
int y = pos.getY();
int z = pos.getZ();

BlockPathTypes type = getMaidBlockPathTypeRaw(level, pos);
if (type == BlockPathTypes.OPEN && y >= level.getMinBuildHeight() + 1) {
BlockPathTypes typeBelow = getMaidBlockPathTypeRaw(level, pos.set(x, y - 1, z));

type = typeBelow != BlockPathTypes.WALKABLE
&& typeBelow != BlockPathTypes.OPEN
&& typeBelow != BlockPathTypes.WATER
&& typeBelow != BlockPathTypes.LAVA ? BlockPathTypes.WALKABLE : BlockPathTypes.OPEN;

if (typeBelow == BlockPathTypes.DAMAGE_FIRE) {
type = BlockPathTypes.DAMAGE_FIRE;
}

if (typeBelow == BlockPathTypes.DAMAGE_CACTUS) {
type = BlockPathTypes.DAMAGE_CACTUS;
}

if (typeBelow == BlockPathTypes.DAMAGE_OTHER) {
type = BlockPathTypes.DAMAGE_OTHER;
}

if (typeBelow == BlockPathTypes.STICKY_HONEY) {
type = BlockPathTypes.STICKY_HONEY;
}

if (typeBelow == BlockPathTypes.POWDER_SNOW) {
type = BlockPathTypes.DANGER_POWDER_SNOW;
}
}

if (type == BlockPathTypes.WALKABLE) {
type = checkNeighbourBlocks(level, pos.set(x, y, z), type);
}

return type;
}

private static BlockPathTypes getMaidBlockPathTypeRaw(BlockGetter level, BlockPos pos) {
BlockState blockState = level.getBlockState(pos);
BlockPathTypes pathType = blockState.getBlockPathType(level, pos, null);
if (pathType != null) {
return pathType;
} else if (blockState.isAir()) {
return BlockPathTypes.OPEN;
} else if (blockState.getBlock() instanceof FenceGateBlock) {
return blockState.getValue(FenceGateBlock.OPEN) ? BlockPathTypes.DOOR_OPEN : BlockPathTypes.DOOR_WOOD_CLOSED;
} else {
return WalkNodeEvaluator.getBlockPathTypeRaw(level, pos);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.github.tartaricacid.touhoulittlemaid.entity.ai.navigation;

import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.pathfinder.PathFinder;

public class MaidPathNavigation extends GroundPathNavigation {
public MaidPathNavigation(Mob mob, Level level) {
super(mob, level);
this.mob.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F);
}

@Override
protected PathFinder createPathFinder(int range) {
this.nodeEvaluator = new MaidNodeEvaluator();
this.nodeEvaluator.setCanOpenDoors(true);
this.nodeEvaluator.setCanPassDoors(true);
this.nodeEvaluator.setCanFloat(true);
return new PathFinder(this.nodeEvaluator, range);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package com.github.tartaricacid.touhoulittlemaid.entity.ai.navigation;

import net.minecraft.MethodsReturnNonnullByDefault;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.github.tartaricacid.touhoulittlemaid.config.subconfig.MaidConfig;
import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.MaidBrain;
import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.MaidSchedule;
import com.github.tartaricacid.touhoulittlemaid.entity.ai.navigation.MaidPathNavigation;
import com.github.tartaricacid.touhoulittlemaid.entity.backpack.*;
import com.github.tartaricacid.touhoulittlemaid.entity.chatbubble.ChatBubbleManger;
import com.github.tartaricacid.touhoulittlemaid.entity.chatbubble.ChatText;
Expand Down Expand Up @@ -83,7 +84,7 @@
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.monster.CrossbowAttackMob;
import net.minecraft.world.entity.player.Player;
Expand All @@ -104,7 +105,6 @@
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.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
Expand Down Expand Up @@ -207,9 +207,6 @@ public class EntityMaid extends TamableAnimal implements CrossbowAttackMob, IMai

protected EntityMaid(EntityType<EntityMaid> type, Level world) {
super(type, world);
((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(true);
this.getNavigation().setCanFloat(true);
this.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F);
this.favorabilityManager = new FavorabilityManager(this);
this.scriptBookManager = new MaidScriptBookManager();
this.schedulePos = new SchedulePos(BlockPos.ZERO, world.dimension().location());
Expand Down Expand Up @@ -258,6 +255,11 @@ protected void defineSynchedData() {
this.entityData.define(GAME_SKILL, new CompoundTag());
}

@Override
protected PathNavigation createNavigation(Level levelIn) {
return new MaidPathNavigation(this, levelIn);
}

@Override
@SuppressWarnings("all")
public Brain<EntityMaid> getBrain() {
Expand Down

0 comments on commit e3691fa

Please sign in to comment.